mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-06 20:32:44 +08:00
refactor: merge DoAction to mcp server tools
This commit is contained in:
@@ -54,6 +54,7 @@ Copyright © 2017-present debugtalk. Apache-2.0 License.
|
||||
* [hrp build](hrp_build.md) - Build plugin for testing
|
||||
* [hrp convert](hrp_convert.md) - Convert multiple source format to HttpRunner JSON/YAML/gotest/pytest cases
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
* [hrp mcp-server](hrp_mcp-server.md) - Start MCP server for UI automation
|
||||
* [hrp mcphost](hrp_mcphost.md) - Start a chat session to interact with MCP tools
|
||||
* [hrp pytest](hrp_pytest.md) - Run API test with pytest
|
||||
* [hrp run](hrp_run.md) - Run API test with go engine
|
||||
@@ -61,4 +62,4 @@ Copyright © 2017-present debugtalk. Apache-2.0 License.
|
||||
* [hrp startproject](hrp_startproject.md) - Create a scaffold project
|
||||
* [hrp wiki](hrp_wiki.md) - visit https://httprunner.com
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -23,4 +23,4 @@ simple utils for android device management
|
||||
* [hrp adb install](hrp_adb_install.md) - push package to the device and install them automatically
|
||||
* [hrp adb screencap](hrp_adb_screencap.md) - Start android screen capture
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -24,4 +24,4 @@ hrp adb devices [flags]
|
||||
|
||||
* [hrp adb](hrp_adb.md) - simple utils for android device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -28,4 +28,4 @@ hrp adb install [flags] PACKAGE
|
||||
|
||||
* [hrp adb](hrp_adb.md) - simple utils for android device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -25,4 +25,4 @@ hrp adb screencap [flags]
|
||||
|
||||
* [hrp adb](hrp_adb.md) - simple utils for android device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -36,4 +36,4 @@ hrp build $path ... [flags]
|
||||
|
||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -34,4 +34,4 @@ hrp convert $path... [flags]
|
||||
|
||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -29,4 +29,4 @@ simple utils for ios device management
|
||||
* [hrp ios uninstall](hrp_ios_uninstall.md) - uninstall package automatically
|
||||
* [hrp ios xctest](hrp_ios_xctest.md) - run xctest
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -26,4 +26,4 @@ hrp ios apps [flags]
|
||||
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -24,4 +24,4 @@ hrp ios devices [flags]
|
||||
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -25,4 +25,4 @@ hrp ios install [flags] PACKAGE
|
||||
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -28,4 +28,4 @@ hrp ios mount [flags]
|
||||
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -26,4 +26,4 @@ hrp ios ps [flags]
|
||||
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -25,4 +25,4 @@ hrp ios reboot [flags]
|
||||
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -24,4 +24,4 @@ hrp ios tunnel [flags]
|
||||
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -26,4 +26,4 @@ hrp ios uninstall [flags] PACKAGE
|
||||
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -28,4 +28,4 @@ hrp ios xctest [flags]
|
||||
|
||||
* [hrp ios](hrp_ios.md) - simple utils for ios device management
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
31
docs/cmd/hrp_mcp-server.md
Normal file
31
docs/cmd/hrp_mcp-server.md
Normal file
@@ -0,0 +1,31 @@
|
||||
## hrp mcp-server
|
||||
|
||||
Start MCP server for UI automation
|
||||
|
||||
### Synopsis
|
||||
|
||||
Start MCP server for UI automation, expose device driver via MCP protocol
|
||||
|
||||
```
|
||||
hrp mcp-server [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for mcp-server
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--log-json set log to json format (default colorized console)
|
||||
-l, --log-level string set log level (default "INFO")
|
||||
--venv string specify python3 venv path
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
@@ -13,10 +13,10 @@ hrp mcphost [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--dump string path to save the exported tools JSON file
|
||||
-h, --help help for mcphost
|
||||
-c, --mcp-config string path to the MCP config file (default "$HOME/.hrp/mcp.json")
|
||||
--system-prompt string path to system prompt JSON file
|
||||
--dump string path to save the exported tools JSON file
|
||||
-h, --help help for mcphost
|
||||
-c, --mcp-config string path to the MCP config file (default "$HOME/.hrp/mcp.json")
|
||||
--with-uixt start built-in uixt MCP server
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -31,4 +31,4 @@ hrp mcphost [flags]
|
||||
|
||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -24,4 +24,4 @@ hrp pytest $path ... [flags]
|
||||
|
||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -44,4 +44,4 @@ hrp run $path... [flags]
|
||||
|
||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -30,4 +30,4 @@ hrp server start [flags]
|
||||
|
||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -29,4 +29,4 @@ hrp startproject $project_name [flags]
|
||||
|
||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -24,4 +24,4 @@ hrp wiki [flags]
|
||||
|
||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
||||
|
||||
###### Auto generated by spf13/cobra on 17-May-2025
|
||||
###### Auto generated by spf13/cobra on 25-May-2025
|
||||
|
||||
@@ -1 +1 @@
|
||||
v5.0.0-beta-2505250810
|
||||
v5.0.0-beta-2505252353
|
||||
|
||||
10
step_ui.go
10
step_ui.go
@@ -212,7 +212,7 @@ func (s *StepMobile) Back() *StepMobile {
|
||||
// Swipe drags from [sx, sy] to [ex, ey]
|
||||
func (s *StepMobile) Swipe(sx, sy, ex, ey float64, opts ...option.ActionOption) *StepMobile {
|
||||
action := uixt.MobileAction{
|
||||
Method: option.ACTION_Swipe,
|
||||
Method: option.ACTION_SwipeCoordinate,
|
||||
Params: []float64{sx, sy, ex, ey},
|
||||
Options: option.NewActionOptions(opts...),
|
||||
}
|
||||
@@ -223,7 +223,7 @@ func (s *StepMobile) Swipe(sx, sy, ex, ey float64, opts ...option.ActionOption)
|
||||
|
||||
func (s *StepMobile) SwipeUp(opts ...option.ActionOption) *StepMobile {
|
||||
action := uixt.MobileAction{
|
||||
Method: option.ACTION_Swipe,
|
||||
Method: option.ACTION_SwipeDirection,
|
||||
Params: "up",
|
||||
Options: option.NewActionOptions(opts...),
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func (s *StepMobile) SwipeUp(opts ...option.ActionOption) *StepMobile {
|
||||
|
||||
func (s *StepMobile) SwipeDown(opts ...option.ActionOption) *StepMobile {
|
||||
action := uixt.MobileAction{
|
||||
Method: option.ACTION_Swipe,
|
||||
Method: option.ACTION_SwipeDirection,
|
||||
Params: "down",
|
||||
Options: option.NewActionOptions(opts...),
|
||||
}
|
||||
@@ -245,7 +245,7 @@ func (s *StepMobile) SwipeDown(opts ...option.ActionOption) *StepMobile {
|
||||
|
||||
func (s *StepMobile) SwipeLeft(opts ...option.ActionOption) *StepMobile {
|
||||
action := uixt.MobileAction{
|
||||
Method: option.ACTION_Swipe,
|
||||
Method: option.ACTION_SwipeDirection,
|
||||
Params: "left",
|
||||
Options: option.NewActionOptions(opts...),
|
||||
}
|
||||
@@ -256,7 +256,7 @@ func (s *StepMobile) SwipeLeft(opts ...option.ActionOption) *StepMobile {
|
||||
|
||||
func (s *StepMobile) SwipeRight(opts ...option.ActionOption) *StepMobile {
|
||||
action := uixt.MobileAction{
|
||||
Method: option.ACTION_Swipe,
|
||||
Method: option.ACTION_SwipeDirection,
|
||||
Params: "right",
|
||||
Options: option.NewActionOptions(opts...),
|
||||
}
|
||||
|
||||
@@ -408,11 +408,11 @@ func (ad *ADBDriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Action
|
||||
Float64("toX", toX).Float64("toY", toY).Msg("ADBDriver.Swipe")
|
||||
|
||||
actionOptions := option.NewActionOptions(opts...)
|
||||
fromX, fromY, toX, toY, err := preHandler_Swipe(ad, actionOptions, fromX, fromY, toX, toY)
|
||||
fromX, fromY, toX, toY, err := preHandler_Swipe(ad, option.ACTION_SwipeCoordinate, actionOptions, fromX, fromY, toX, toY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer postHandler(ad, option.ACTION_Swipe, actionOptions)
|
||||
defer postHandler(ad, option.ACTION_SwipeCoordinate, actionOptions)
|
||||
|
||||
// adb shell input swipe fromX fromY toX toY
|
||||
_, err = ad.runShellCommand(
|
||||
|
||||
@@ -394,11 +394,11 @@ func (ud *UIA2Driver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Actio
|
||||
Float64("toX", toX).Float64("toY", toY).Msg("UIA2Driver.Swipe")
|
||||
|
||||
actionOptions := option.NewActionOptions(opts...)
|
||||
fromX, fromY, toX, toY, err := preHandler_Swipe(ud, actionOptions, fromX, fromY, toX, toY)
|
||||
fromX, fromY, toX, toY, err := preHandler_Swipe(ud, option.ACTION_SwipeCoordinate, actionOptions, fromX, fromY, toX, toY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer postHandler(ud, option.ACTION_Swipe, actionOptions)
|
||||
defer postHandler(ud, option.ACTION_SwipeCoordinate, actionOptions)
|
||||
|
||||
duration := 200.0
|
||||
if actionOptions.PressDuration > 0 {
|
||||
|
||||
@@ -62,7 +62,7 @@ func (dExt *XTDriver) AIAction(text string, opts ...option.ActionOption) error {
|
||||
},
|
||||
}
|
||||
|
||||
_, err = dExt.client.CallTool(context.Background(), req)
|
||||
_, err = dExt.Client.CallTool(context.Background(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ func MarkUIOperation(driver IDriver, actionType option.ActionMethod, actionCoord
|
||||
x, y := actionCoordinates[0], actionCoordinates[1]
|
||||
point := image.Point{X: int(x), Y: int(y)}
|
||||
err = SaveImageWithCircleMarker(compressedBufSource, point, imagePath)
|
||||
} else if actionType == option.ACTION_Swipe || actionType == option.ACTION_Drag {
|
||||
} else if actionType == option.ACTION_SwipeDirection || actionType == option.ACTION_SwipeCoordinate || actionType == option.ACTION_Drag {
|
||||
if len(actionCoordinates) != 4 {
|
||||
return fmt.Errorf("invalid swipe action coordinates: %v", actionCoordinates)
|
||||
}
|
||||
|
||||
@@ -98,7 +98,8 @@ func preHandler_Drag(driver IDriver, options *option.ActionOptions, rawFomX, raw
|
||||
return fromX, fromY, toX, toY, nil
|
||||
}
|
||||
|
||||
func preHandler_Swipe(driver IDriver, options *option.ActionOptions, rawFomX, rawFromY, rawToX, rawToY float64) (
|
||||
func preHandler_Swipe(driver IDriver, actionType option.ActionMethod,
|
||||
options *option.ActionOptions, rawFomX, rawFromY, rawToX, rawToY float64) (
|
||||
fromX, fromY, toX, toY float64, err error) {
|
||||
|
||||
fromX, fromY, toX, toY, err = convertToAbsoluteCoordinates(driver, rawFomX, rawFromY, rawToX, rawToY)
|
||||
@@ -109,7 +110,7 @@ func preHandler_Swipe(driver IDriver, options *option.ActionOptions, rawFomX, ra
|
||||
|
||||
// save screenshot before action and mark UI operation
|
||||
if options.PreMarkOperation {
|
||||
if markErr := MarkUIOperation(driver, option.ACTION_Swipe, []float64{fromX, fromY, toX, toY}); markErr != nil {
|
||||
if markErr := MarkUIOperation(driver, actionType, []float64{fromX, fromY, toX, toY}); markErr != nil {
|
||||
log.Warn().Err(markErr).Msg("Failed to mark swipe operation")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,11 +187,11 @@ func (hd *HDCDriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Action
|
||||
Float64("toX", toX).Float64("toY", toY).Msg("HDCDriver.Swipe")
|
||||
|
||||
actionOptions := option.NewActionOptions(opts...)
|
||||
fromX, fromY, toX, toY, err := preHandler_Swipe(hd, actionOptions, fromX, fromY, toX, toY)
|
||||
fromX, fromY, toX, toY, err := preHandler_Swipe(hd, option.ACTION_SwipeCoordinate, actionOptions, fromX, fromY, toX, toY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer postHandler(hd, option.ACTION_Swipe, actionOptions)
|
||||
defer postHandler(hd, option.ACTION_SwipeCoordinate, actionOptions)
|
||||
|
||||
duration := 200
|
||||
if actionOptions.PressDuration > 0 {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,8 @@ func TestNewMCPServer(t *testing.T) {
|
||||
"list_available_devices",
|
||||
"select_device",
|
||||
"list_packages",
|
||||
"launch_app",
|
||||
"terminate_app",
|
||||
"app_launch",
|
||||
"app_terminate",
|
||||
"get_screen_size",
|
||||
"press_button",
|
||||
"tap_xy",
|
||||
@@ -54,7 +54,7 @@ func TestToolInterfaces(t *testing.T) {
|
||||
&ToolGetScreenSize{},
|
||||
&ToolPressButton{},
|
||||
&ToolTapXY{},
|
||||
&ToolSwipe{},
|
||||
&ToolSwipeDirection{},
|
||||
&ToolDrag{},
|
||||
&ToolScreenShot{},
|
||||
&ToolHome{},
|
||||
@@ -64,7 +64,7 @@ func TestToolInterfaces(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tool := range tools {
|
||||
assert.NotEmpty(t, tool.Name(), "Tool name should not be empty")
|
||||
assert.NotEmpty(t, string(tool.Name()), "Tool name should not be empty")
|
||||
assert.NotEmpty(t, tool.Description(), "Tool description should not be empty")
|
||||
assert.NotNil(t, tool.Options(), "Tool options should not be nil")
|
||||
assert.NotNil(t, tool.Implement(), "Tool implementation should not be nil")
|
||||
|
||||
@@ -12,15 +12,17 @@ type ActionMethod string
|
||||
|
||||
const (
|
||||
ACTION_LOG ActionMethod = "log"
|
||||
ACTION_AppInstall ActionMethod = "install"
|
||||
ACTION_AppUninstall ActionMethod = "uninstall"
|
||||
ACTION_WebLoginNoneUI ActionMethod = "login_none_ui"
|
||||
ACTION_ListPackages ActionMethod = "list_packages"
|
||||
ACTION_AppInstall ActionMethod = "app_install"
|
||||
ACTION_AppUninstall ActionMethod = "app_uninstall"
|
||||
ACTION_WebLoginNoneUI ActionMethod = "web_login_none_ui"
|
||||
ACTION_AppClear ActionMethod = "app_clear"
|
||||
ACTION_AppStart ActionMethod = "app_start"
|
||||
ACTION_AppLaunch ActionMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
|
||||
ACTION_AppTerminate ActionMethod = "app_terminate"
|
||||
ACTION_AppStop ActionMethod = "app_stop"
|
||||
ACTION_ScreenShot ActionMethod = "screenshot"
|
||||
ACTION_GetScreenSize ActionMethod = "get_screen_size"
|
||||
ACTION_Sleep ActionMethod = "sleep"
|
||||
ACTION_SleepMS ActionMethod = "sleep_ms"
|
||||
ACTION_SleepRandom ActionMethod = "sleep_random"
|
||||
@@ -33,12 +35,14 @@ const (
|
||||
ACTION_Home ActionMethod = "home"
|
||||
ACTION_TapXY ActionMethod = "tap_xy"
|
||||
ACTION_TapAbsXY ActionMethod = "tap_abs_xy"
|
||||
ACTION_TapByOCR ActionMethod = "tap_ocr"
|
||||
ACTION_TapByCV ActionMethod = "tap_cv"
|
||||
ACTION_TapByOCR ActionMethod = "tap_by_ocr"
|
||||
ACTION_TapByCV ActionMethod = "tap_by_cv"
|
||||
ACTION_DoubleTapXY ActionMethod = "double_tap_xy"
|
||||
ACTION_Swipe ActionMethod = "swipe"
|
||||
ACTION_SwipeDirection ActionMethod = "swipe_direction" // swipe by direction (up, down, left, right)
|
||||
ACTION_SwipeCoordinate ActionMethod = "swipe_coordinate" // swipe by coordinates (fromX, fromY, toX, toY)
|
||||
ACTION_Drag ActionMethod = "drag"
|
||||
ACTION_Input ActionMethod = "input"
|
||||
ACTION_PressButton ActionMethod = "press_button"
|
||||
ACTION_Back ActionMethod = "back"
|
||||
ACTION_KeyCode ActionMethod = "keycode"
|
||||
ACTION_AIAction ActionMethod = "ai_action" // action with ai
|
||||
@@ -49,6 +53,10 @@ const (
|
||||
ACTION_SecondaryClickBySelector ActionMethod = "secondary_click_by_selector"
|
||||
ACTION_GetElementTextBySelector ActionMethod = "get_element_text_by_selector"
|
||||
|
||||
// device actions
|
||||
ACTION_ListAvailableDevices ActionMethod = "list_available_devices"
|
||||
ACTION_SelectDevice ActionMethod = "select_device"
|
||||
|
||||
// custom actions
|
||||
ACTION_SwipeToTapApp ActionMethod = "swipe_to_tap_app" // swipe left & right to find app and tap
|
||||
ACTION_SwipeToTapText ActionMethod = "swipe_to_tap_text" // swipe up & down to find text and tap
|
||||
|
||||
757
uixt/sdk.go
757
uixt/sdk.go
@@ -2,10 +2,8 @@ package uixt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v5/uixt/ai"
|
||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||
"github.com/mark3labs/mcp-go/client"
|
||||
@@ -16,7 +14,7 @@ import (
|
||||
func NewXTDriver(driver IDriver, opts ...option.AIServiceOption) (*XTDriver, error) {
|
||||
driverExt := &XTDriver{
|
||||
IDriver: driver,
|
||||
client: &MCPClient4XTDriver{
|
||||
Client: &MCPClient4XTDriver{
|
||||
Server: NewMCPServer(),
|
||||
},
|
||||
}
|
||||
@@ -48,7 +46,7 @@ type XTDriver struct {
|
||||
CVService ai.ICVService // OCR/CV
|
||||
LLMService ai.ILLMService // LLM
|
||||
|
||||
client *MCPClient4XTDriver // MCP Client
|
||||
Client *MCPClient4XTDriver // MCP Client
|
||||
}
|
||||
|
||||
// MCPClient4XTDriver is a mock MCP client that only implements the methods used by the host
|
||||
@@ -63,10 +61,11 @@ func (c *MCPClient4XTDriver) ListTools(ctx context.Context, req mcp.ListToolsReq
|
||||
}
|
||||
|
||||
func (c *MCPClient4XTDriver) CallTool(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
handler := c.Server.GetHandler(req.Params.Name)
|
||||
if handler == nil {
|
||||
return mcp.NewToolResultError(fmt.Sprintf("handler for tool %s not found", req.Params.Name)), nil
|
||||
actionTool := c.Server.GetToolByActionMethod(option.ActionMethod(req.Params.Name))
|
||||
if actionTool == nil {
|
||||
return mcp.NewToolResultError(fmt.Sprintf("action %s for tool not found", req.Params.Name)), nil
|
||||
}
|
||||
handler := actionTool.Implement()
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
@@ -81,14 +80,20 @@ func (c *MCPClient4XTDriver) Close() error {
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) ExecuteAction(action MobileAction) (err error) {
|
||||
// Convert action to MCP tool call
|
||||
req, err := convertActionToCallToolRequest(action)
|
||||
// Find the corresponding tool for this action method
|
||||
tool := dExt.Client.Server.GetToolByActionMethod(action.Method)
|
||||
if tool == nil {
|
||||
return fmt.Errorf("no tool found for action method: %s", action.Method)
|
||||
}
|
||||
|
||||
// Use the tool's own conversion method
|
||||
req, err := tool.ConvertActionToCallToolRequest(action)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert action to MCP tool call: %w", err)
|
||||
}
|
||||
|
||||
// Execute via MCP tool
|
||||
result, err := dExt.client.CallTool(context.Background(), req)
|
||||
result, err := dExt.Client.CallTool(context.Background(), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("MCP tool call failed: %w", err)
|
||||
}
|
||||
@@ -101,734 +106,8 @@ func (dExt *XTDriver) ExecuteAction(action MobileAction) (err error) {
|
||||
return fmt.Errorf("tool execution failed")
|
||||
}
|
||||
|
||||
log.Debug().Str("method", string(action.Method)).Msg("executed action via MCP tool")
|
||||
log.Debug().Str("method", string(action.Method)).
|
||||
Str("tool", string(tool.Name())).
|
||||
Msg("executed action via MCP tool")
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertActionToCallToolRequest(action MobileAction) (mcp.CallToolRequest, error) {
|
||||
var arguments map[string]interface{}
|
||||
|
||||
switch action.Method {
|
||||
case option.ACTION_WebLoginNoneUI:
|
||||
if params, ok := action.Params.([]interface{}); ok && len(params) == 4 {
|
||||
arguments = map[string]interface{}{
|
||||
"packageName": params[0].(string),
|
||||
"phoneNumber": params[1].(string),
|
||||
"captcha": params[2].(string),
|
||||
"password": params[3].(string),
|
||||
}
|
||||
} else if params, ok := action.Params.([]string); ok && len(params) == 4 {
|
||||
arguments = map[string]interface{}{
|
||||
"packageName": params[0],
|
||||
"phoneNumber": params[1],
|
||||
"captcha": params[2],
|
||||
"password": params[3],
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid web login params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "web_login_none_ui",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_AppInstall:
|
||||
if app, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"appUrl": app,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid app install params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "app_install",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_AppUninstall:
|
||||
if packageName, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"packageName": packageName,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid app uninstall params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "app_uninstall",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_AppClear:
|
||||
if packageName, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"packageName": packageName,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid app clear params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "app_clear",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_AppLaunch:
|
||||
if packageName, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"packageName": packageName,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid app launch params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "launch_app",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_SwipeToTapApp:
|
||||
if appName, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"appName": appName,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid swipe to tap app params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "swipe_to_tap_app",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_SwipeToTapText:
|
||||
if text, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"text": text,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid swipe to tap text params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "swipe_to_tap_text",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_SwipeToTapTexts:
|
||||
var texts []string
|
||||
if textsSlice, ok := action.Params.([]string); ok {
|
||||
texts = textsSlice
|
||||
} else if textsInterface, err := builtin.ConvertToStringSlice(action.Params); err == nil {
|
||||
texts = textsInterface
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid swipe to tap texts params: %v", action.Params)
|
||||
}
|
||||
arguments = map[string]interface{}{
|
||||
"texts": texts,
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "swipe_to_tap_texts",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_AppTerminate:
|
||||
if packageName, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"packageName": packageName,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid app terminate params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "terminate_app",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_Home:
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "home",
|
||||
Arguments: map[string]interface{}{},
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_SecondaryClick:
|
||||
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil && len(params) == 2 {
|
||||
arguments = map[string]interface{}{
|
||||
"x": params[0],
|
||||
"y": params[1],
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid secondary click params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "secondary_click",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_HoverBySelector:
|
||||
if selector, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"selector": selector,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid hover by selector params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "hover_by_selector",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_TapBySelector:
|
||||
if selector, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"selector": selector,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid tap by selector params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "tap_by_selector",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_SecondaryClickBySelector:
|
||||
if selector, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"selector": selector,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid secondary click by selector params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "secondary_click_by_selector",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_WebCloseTab:
|
||||
var tabIndex int
|
||||
if param, ok := action.Params.(json.Number); ok {
|
||||
paramInt64, _ := param.Int64()
|
||||
tabIndex = int(paramInt64)
|
||||
} else if param, ok := action.Params.(int64); ok {
|
||||
tabIndex = int(param)
|
||||
} else if param, ok := action.Params.(int); ok {
|
||||
tabIndex = param
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid web close tab params: %v", action.Params)
|
||||
}
|
||||
arguments = map[string]interface{}{
|
||||
"tabIndex": tabIndex,
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "web_close_tab",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_SetIme:
|
||||
if ime, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"ime": ime,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid set ime params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "set_ime",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_GetSource:
|
||||
if packageName, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"packageName": packageName,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid get source params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "get_source",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_TapXY:
|
||||
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil && len(params) == 2 {
|
||||
x, y := params[0], params[1]
|
||||
arguments = map[string]interface{}{
|
||||
"x": x,
|
||||
"y": y,
|
||||
}
|
||||
// Add duration if available from action options
|
||||
if actionOptions := action.GetOptions(); len(actionOptions) > 0 {
|
||||
for _, opt := range actionOptions {
|
||||
if opt != nil {
|
||||
// Add options like duration
|
||||
if duration := action.ActionOptions.Duration; duration > 0 {
|
||||
arguments["duration"] = duration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid tap params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "tap_xy",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_TapAbsXY:
|
||||
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil && len(params) == 2 {
|
||||
x, y := params[0], params[1]
|
||||
arguments = map[string]interface{}{
|
||||
"x": x,
|
||||
"y": y,
|
||||
}
|
||||
// Add duration if available
|
||||
if duration := action.ActionOptions.Duration; duration > 0 {
|
||||
arguments["duration"] = duration
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid tap abs params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "tap_abs_xy",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_TapByOCR:
|
||||
if text, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"text": text,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid tap by OCR params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "tap_by_ocr",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_TapByCV:
|
||||
// For TapByCV, the original action might not have params but relies on options
|
||||
arguments = map[string]interface{}{
|
||||
"imagePath": "", // Will be handled by the tool based on UI types
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "tap_by_cv",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_DoubleTapXY:
|
||||
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil && len(params) == 2 {
|
||||
x, y := params[0], params[1]
|
||||
arguments = map[string]interface{}{
|
||||
"x": x,
|
||||
"y": y,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid double tap params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "double_tap_xy",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_Swipe:
|
||||
// Handle different types of swipe params
|
||||
switch params := action.Params.(type) {
|
||||
case string:
|
||||
// Direction swipe like "up", "down", "left", "right"
|
||||
arguments = map[string]interface{}{
|
||||
"direction": params,
|
||||
}
|
||||
// Add duration and press duration from options
|
||||
if duration := action.ActionOptions.Duration; duration > 0 {
|
||||
arguments["duration"] = duration
|
||||
}
|
||||
if pressDuration := action.ActionOptions.PressDuration; pressDuration > 0 {
|
||||
arguments["pressDuration"] = pressDuration
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "swipe",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
// Advanced swipe with coordinates
|
||||
if paramSlice, err := builtin.ConvertToFloat64Slice(params); err == nil && len(paramSlice) == 4 {
|
||||
arguments = map[string]interface{}{
|
||||
"fromX": paramSlice[0],
|
||||
"fromY": paramSlice[1],
|
||||
"toX": paramSlice[2],
|
||||
"toY": paramSlice[3],
|
||||
}
|
||||
// Add duration and press duration from options
|
||||
if duration := action.ActionOptions.Duration; duration > 0 {
|
||||
arguments["duration"] = duration
|
||||
}
|
||||
if pressDuration := action.ActionOptions.PressDuration; pressDuration > 0 {
|
||||
arguments["pressDuration"] = pressDuration
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "swipe_advanced",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid swipe params: %v", action.Params)
|
||||
|
||||
case option.ACTION_Input:
|
||||
text := fmt.Sprintf("%v", action.Params)
|
||||
arguments = map[string]interface{}{
|
||||
"text": text,
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "input",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_Back:
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "back",
|
||||
Arguments: map[string]interface{}{},
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_Sleep:
|
||||
arguments = map[string]interface{}{
|
||||
"seconds": action.Params,
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "sleep",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_SleepMS:
|
||||
var milliseconds int64
|
||||
if param, ok := action.Params.(json.Number); ok {
|
||||
milliseconds, _ = param.Int64()
|
||||
} else if param, ok := action.Params.(int64); ok {
|
||||
milliseconds = param
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep ms params: %v", action.Params)
|
||||
}
|
||||
arguments = map[string]interface{}{
|
||||
"milliseconds": milliseconds,
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "sleep_ms",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_SleepRandom:
|
||||
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil {
|
||||
arguments = map[string]interface{}{
|
||||
"params": params,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep random params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "sleep_random",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_ScreenShot:
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "screenshot",
|
||||
Arguments: map[string]interface{}{},
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_ClosePopups:
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "close_popups",
|
||||
Arguments: map[string]interface{}{},
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_CallFunction:
|
||||
if description, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"description": description,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid call function params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "call_function",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
case option.ACTION_AIAction:
|
||||
if prompt, ok := action.Params.(string); ok {
|
||||
arguments = map[string]interface{}{
|
||||
"prompt": prompt,
|
||||
}
|
||||
} else {
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid AI action params: %v", action.Params)
|
||||
}
|
||||
return mcp.CallToolRequest{
|
||||
Params: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Meta *struct {
|
||||
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
|
||||
} `json:"_meta,omitempty"`
|
||||
}{
|
||||
Name: "ai_action",
|
||||
Arguments: arguments,
|
||||
},
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return mcp.CallToolRequest{}, fmt.Errorf("unsupported action method: %s", action.Method)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user