mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-08 08:57:40 +08:00
refactor: move uixt mcp server
This commit is contained in:
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/httprunner/httprunner/v5/mcphost"
|
"github.com/httprunner/httprunner/v5/uixt"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ var CmdMCPServer = &cobra.Command{
|
|||||||
Short: "Start MCP server for UI automation",
|
Short: "Start MCP server for UI automation",
|
||||||
Long: `Start MCP server for UI automation, expose device driver via MCP protocol`,
|
Long: `Start MCP server for UI automation, expose device driver via MCP protocol`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
mcpServer := mcphost.NewMCPServer()
|
mcpServer := uixt.NewMCPServer()
|
||||||
return mcpServer.Start()
|
return mcpServer.Start()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2505241028
|
v5.0.0-beta-2505241046
|
||||||
|
|||||||
+2
-2
@@ -69,8 +69,8 @@ func (h *MCPHost) InitServers(ctx context.Context) error {
|
|||||||
// initialize uixt MCP server
|
// initialize uixt MCP server
|
||||||
if h.withUIXT {
|
if h.withUIXT {
|
||||||
h.connections["uixt"] = &Connection{
|
h.connections["uixt"] = &Connection{
|
||||||
Client: &MCPClient4XTDriver{
|
Client: &uixt.MCPClient4XTDriver{
|
||||||
server: NewMCPServer(),
|
Server: uixt.NewMCPServer(),
|
||||||
},
|
},
|
||||||
Config: nil,
|
Config: nil,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package mcphost
|
package uixt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/danielpaulus/go-ios/ios"
|
"github.com/danielpaulus/go-ios/ios"
|
||||||
"github.com/httprunner/httprunner/v5/internal/version"
|
"github.com/httprunner/httprunner/v5/internal/version"
|
||||||
"github.com/httprunner/httprunner/v5/pkg/gadb"
|
"github.com/httprunner/httprunner/v5/pkg/gadb"
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||||
"github.com/mark3labs/mcp-go/client"
|
"github.com/mark3labs/mcp-go/client"
|
||||||
@@ -23,16 +22,16 @@ import (
|
|||||||
// MCPClient4XTDriver is a minimal MCP client that only implements the methods used by the host
|
// MCPClient4XTDriver is a minimal MCP client that only implements the methods used by the host
|
||||||
type MCPClient4XTDriver struct {
|
type MCPClient4XTDriver struct {
|
||||||
client.MCPClient
|
client.MCPClient
|
||||||
server *MCPServer4XTDriver
|
Server *MCPServer4XTDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MCPClient4XTDriver) ListTools(ctx context.Context, req mcp.ListToolsRequest) (*mcp.ListToolsResult, error) {
|
func (c *MCPClient4XTDriver) ListTools(ctx context.Context, req mcp.ListToolsRequest) (*mcp.ListToolsResult, error) {
|
||||||
tools := c.server.ListTools()
|
tools := c.Server.ListTools()
|
||||||
return &mcp.ListToolsResult{Tools: tools}, nil
|
return &mcp.ListToolsResult{Tools: tools}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MCPClient4XTDriver) CallTool(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
func (c *MCPClient4XTDriver) CallTool(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
handler := c.server.GetHandler(req.Params.Name)
|
handler := c.Server.GetHandler(req.Params.Name)
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
return mcp.NewToolResultError(fmt.Sprintf("handler for tool %s not found", req.Params.Name)), nil
|
return mcp.NewToolResultError(fmt.Sprintf("handler for tool %s not found", req.Params.Name)), nil
|
||||||
}
|
}
|
||||||
@@ -49,16 +48,6 @@ func (c *MCPClient4XTDriver) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type toolCall = func(context.Context, mcp.CallToolRequest) (*mcp.CallToolResult, error)
|
|
||||||
|
|
||||||
// MCPServer4XTDriver wraps a MCPServer to expose XTDriver functionality via MCP protocol.
|
|
||||||
type MCPServer4XTDriver struct {
|
|
||||||
mcpServer *server.MCPServer
|
|
||||||
driverCache sync.Map // key is serial, value is *XTDriver
|
|
||||||
tools []mcp.Tool // tools list for uixt
|
|
||||||
handlerMap map[string]toolCall // tool name to handler
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMCPServer creates a new MCP server for XTDriver and registers all tools.
|
// NewMCPServer creates a new MCP server for XTDriver and registers all tools.
|
||||||
func NewMCPServer() *MCPServer4XTDriver {
|
func NewMCPServer() *MCPServer4XTDriver {
|
||||||
mcpServer := server.NewMCPServer(
|
mcpServer := server.NewMCPServer(
|
||||||
@@ -74,12 +63,45 @@ func NewMCPServer() *MCPServer4XTDriver {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type toolCall = func(context.Context, mcp.CallToolRequest) (*mcp.CallToolResult, error)
|
||||||
|
|
||||||
|
// MCPServer4XTDriver wraps a MCPServer to expose XTDriver functionality via MCP protocol.
|
||||||
|
type MCPServer4XTDriver struct {
|
||||||
|
mcpServer *server.MCPServer
|
||||||
|
driverCache sync.Map // key is serial, value is *XTDriver
|
||||||
|
tools []mcp.Tool // tools list for uixt
|
||||||
|
handlerMap map[string]toolCall // tool name to handler
|
||||||
|
}
|
||||||
|
|
||||||
// Start runs the MCP server (blocking).
|
// Start runs the MCP server (blocking).
|
||||||
func (s *MCPServer4XTDriver) Start() error {
|
func (s *MCPServer4XTDriver) Start() error {
|
||||||
log.Info().Msg("Starting HttpRunner UIXT MCP Server...")
|
log.Info().Msg("Starting HttpRunner UIXT MCP Server...")
|
||||||
return server.ServeStdio(s.mcpServer)
|
return server.ServeStdio(s.mcpServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListTools returns all registered tools
|
||||||
|
func (s *MCPServer4XTDriver) ListTools() []mcp.Tool {
|
||||||
|
return s.tools
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTool returns a pointer to the mcp.Tool with the given name
|
||||||
|
func (s *MCPServer4XTDriver) GetTool(name string) *mcp.Tool {
|
||||||
|
for i := range s.tools {
|
||||||
|
if s.tools[i].Name == name {
|
||||||
|
return &s.tools[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHandler returns the tool handler for the given name
|
||||||
|
func (s *MCPServer4XTDriver) GetHandler(name string) toolCall {
|
||||||
|
if s.handlerMap == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return s.handlerMap[name]
|
||||||
|
}
|
||||||
|
|
||||||
// addTools registers all MCP tools.
|
// addTools registers all MCP tools.
|
||||||
func (ums *MCPServer4XTDriver) addTools() {
|
func (ums *MCPServer4XTDriver) addTools() {
|
||||||
// ListAvailableDevices Tool
|
// ListAvailableDevices Tool
|
||||||
@@ -203,7 +225,7 @@ func (ums *MCPServer4XTDriver) handleListAvailableDevices(ctx context.Context, r
|
|||||||
if iosDevices, err := ios.ListDevices(); err == nil {
|
if iosDevices, err := ios.ListDevices(); err == nil {
|
||||||
serialList := make([]string, 0, len(iosDevices.DeviceList))
|
serialList := make([]string, 0, len(iosDevices.DeviceList))
|
||||||
for _, dev := range iosDevices.DeviceList {
|
for _, dev := range iosDevices.DeviceList {
|
||||||
device, err := uixt.NewIOSDevice(
|
device, err := NewIOSDevice(
|
||||||
option.WithUDID(dev.Properties.SerialNumber))
|
option.WithUDID(dev.Properties.SerialNumber))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
@@ -414,7 +436,7 @@ func (ums *MCPServer4XTDriver) handleScreenShot(ctx context.Context, request mcp
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferBase64, err := uixt.GetScreenShotBufferBase64(driverExt.IDriver)
|
bufferBase64, err := GetScreenShotBufferBase64(driverExt.IDriver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("ScreenShot failed")
|
log.Error().Err(err).Msg("ScreenShot failed")
|
||||||
return mcp.NewToolResultError(fmt.Sprintf("Failed to take screenshot: %v", err)), nil
|
return mcp.NewToolResultError(fmt.Sprintf("Failed to take screenshot: %v", err)), nil
|
||||||
@@ -425,7 +447,7 @@ func (ums *MCPServer4XTDriver) handleScreenShot(ctx context.Context, request mcp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setupXTDriver initializes an XTDriver based on the platform and serial.
|
// setupXTDriver initializes an XTDriver based on the platform and serial.
|
||||||
func (ums *MCPServer4XTDriver) setupXTDriver(_ context.Context, args map[string]interface{}) (*uixt.XTDriver, error) {
|
func (ums *MCPServer4XTDriver) setupXTDriver(_ context.Context, args map[string]interface{}) (*XTDriver, error) {
|
||||||
platform, _ := args["platform"].(string)
|
platform, _ := args["platform"].(string)
|
||||||
serial, _ := args["serial"].(string)
|
serial, _ := args["serial"].(string)
|
||||||
if platform == "" {
|
if platform == "" {
|
||||||
@@ -436,7 +458,7 @@ func (ums *MCPServer4XTDriver) setupXTDriver(_ context.Context, args map[string]
|
|||||||
// Check if driver exists in cache
|
// Check if driver exists in cache
|
||||||
cacheKey := fmt.Sprintf("%s_%s", platform, serial)
|
cacheKey := fmt.Sprintf("%s_%s", platform, serial)
|
||||||
if cachedDriver, ok := ums.driverCache.Load(cacheKey); ok {
|
if cachedDriver, ok := ums.driverCache.Load(cacheKey); ok {
|
||||||
if driverExt, ok := cachedDriver.(*uixt.XTDriver); ok {
|
if driverExt, ok := cachedDriver.(*XTDriver); ok {
|
||||||
log.Info().Str("platform", platform).Str("serial", serial).Msg("Using cached driver")
|
log.Info().Str("platform", platform).Str("serial", serial).Msg("Using cached driver")
|
||||||
return driverExt, nil
|
return driverExt, nil
|
||||||
}
|
}
|
||||||
@@ -451,22 +473,22 @@ func (ums *MCPServer4XTDriver) setupXTDriver(_ context.Context, args map[string]
|
|||||||
return driverExt, nil
|
return driverExt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDriverExt(platform, serial string) (*uixt.XTDriver, error) {
|
func initDriverExt(platform, serial string) (*XTDriver, error) {
|
||||||
// init device
|
// init device
|
||||||
var device uixt.IDevice
|
var device IDevice
|
||||||
var err error
|
var err error
|
||||||
switch strings.ToLower(platform) {
|
switch strings.ToLower(platform) {
|
||||||
case "android":
|
case "android":
|
||||||
device, err = uixt.NewAndroidDevice(option.WithSerialNumber(serial))
|
device, err = NewAndroidDevice(option.WithSerialNumber(serial))
|
||||||
case "ios":
|
case "ios":
|
||||||
device, err = uixt.NewIOSDevice(
|
device, err = NewIOSDevice(
|
||||||
option.WithUDID(serial),
|
option.WithUDID(serial),
|
||||||
option.WithWDAPort(8700),
|
option.WithWDAPort(8700),
|
||||||
option.WithWDAMjpegPort(8800),
|
option.WithWDAMjpegPort(8800),
|
||||||
option.WithResetHomeOnStartup(false),
|
option.WithResetHomeOnStartup(false),
|
||||||
)
|
)
|
||||||
case "browser":
|
case "browser":
|
||||||
device, err = uixt.NewBrowserDevice(option.WithBrowserID(serial))
|
device, err = NewBrowserDevice(option.WithBrowserID(serial))
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid platform: %s", platform)
|
return nil, fmt.Errorf("invalid platform: %s", platform)
|
||||||
}
|
}
|
||||||
@@ -487,7 +509,7 @@ func initDriverExt(platform, serial string) (*uixt.XTDriver, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init XTDriver
|
// init XTDriver
|
||||||
driverExt, err := uixt.NewXTDriver(driver,
|
driverExt, err := NewXTDriver(driver,
|
||||||
option.WithCVService(option.CVServiceTypeVEDEM))
|
option.WithCVService(option.CVServiceTypeVEDEM))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("init XT driver failed: %w", err)
|
return nil, fmt.Errorf("init XT driver failed: %w", err)
|
||||||
@@ -549,26 +571,3 @@ var commonToolOptions = []mcp.ToolOption{
|
|||||||
mcp.WithString("platform", mcp.Required(), mcp.Description("Device platform: android/ios/browser")),
|
mcp.WithString("platform", mcp.Required(), mcp.Description("Device platform: android/ios/browser")),
|
||||||
mcp.WithString("serial", mcp.Required(), mcp.Description("Device serial/udid/browser id")),
|
mcp.WithString("serial", mcp.Required(), mcp.Description("Device serial/udid/browser id")),
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListTools returns all registered tools
|
|
||||||
func (s *MCPServer4XTDriver) ListTools() []mcp.Tool {
|
|
||||||
return s.tools
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTool returns a pointer to the mcp.Tool with the given name
|
|
||||||
func (s *MCPServer4XTDriver) GetTool(name string) *mcp.Tool {
|
|
||||||
for i := range s.tools {
|
|
||||||
if s.tools[i].Name == name {
|
|
||||||
return &s.tools[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHandler returns the tool handler for the given name
|
|
||||||
func (s *MCPServer4XTDriver) GetHandler(name string) toolCall {
|
|
||||||
if s.handlerMap == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return s.handlerMap[name]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user