mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 23:51:25 +08:00
refactor: merge DoAction to mcp server tools
This commit is contained in:
+2
-1
@@ -54,6 +54,7 @@ Copyright © 2017-present debugtalk. Apache-2.0 License.
|
|||||||
* [hrp build](hrp_build.md) - Build plugin for testing
|
* [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 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 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 mcphost](hrp_mcphost.md) - Start a chat session to interact with MCP tools
|
||||||
* [hrp pytest](hrp_pytest.md) - Run API test with pytest
|
* [hrp pytest](hrp_pytest.md) - Run API test with pytest
|
||||||
* [hrp run](hrp_run.md) - Run API test with go engine
|
* [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 startproject](hrp_startproject.md) - Create a scaffold project
|
||||||
* [hrp wiki](hrp_wiki.md) - visit https://httprunner.com
|
* [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
|
||||||
|
|||||||
+1
-1
@@ -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 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
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
@@ -29,4 +29,4 @@ simple utils for ios device management
|
|||||||
* [hrp ios uninstall](hrp_ios_uninstall.md) - uninstall package automatically
|
* [hrp ios uninstall](hrp_ios_uninstall.md) - uninstall package automatically
|
||||||
* [hrp ios xctest](hrp_ios_xctest.md) - run xctest
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -16,7 +16,7 @@ hrp mcphost [flags]
|
|||||||
--dump string path to save the exported tools JSON file
|
--dump string path to save the exported tools JSON file
|
||||||
-h, --help help for mcphost
|
-h, --help help for mcphost
|
||||||
-c, --mcp-config string path to the MCP config file (default "$HOME/.hrp/mcp.json")
|
-c, --mcp-config string path to the MCP config file (default "$HOME/.hrp/mcp.json")
|
||||||
--system-prompt string path to system prompt JSON file
|
--with-uixt start built-in uixt MCP server
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### 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
|
* [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
|
* [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
@@ -44,4 +44,4 @@ hrp run $path... [flags]
|
|||||||
|
|
||||||
* [hrp](hrp.md) - All-in-One Testing Framework for API, UI and Performance
|
* [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
|
* [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
|
* [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
|
* [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
|
||||||
|
|||||||
+5
-5
@@ -212,7 +212,7 @@ func (s *StepMobile) Back() *StepMobile {
|
|||||||
// Swipe drags from [sx, sy] to [ex, ey]
|
// Swipe drags from [sx, sy] to [ex, ey]
|
||||||
func (s *StepMobile) Swipe(sx, sy, ex, ey float64, opts ...option.ActionOption) *StepMobile {
|
func (s *StepMobile) Swipe(sx, sy, ex, ey float64, opts ...option.ActionOption) *StepMobile {
|
||||||
action := uixt.MobileAction{
|
action := uixt.MobileAction{
|
||||||
Method: option.ACTION_Swipe,
|
Method: option.ACTION_SwipeCoordinate,
|
||||||
Params: []float64{sx, sy, ex, ey},
|
Params: []float64{sx, sy, ex, ey},
|
||||||
Options: option.NewActionOptions(opts...),
|
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 {
|
func (s *StepMobile) SwipeUp(opts ...option.ActionOption) *StepMobile {
|
||||||
action := uixt.MobileAction{
|
action := uixt.MobileAction{
|
||||||
Method: option.ACTION_Swipe,
|
Method: option.ACTION_SwipeDirection,
|
||||||
Params: "up",
|
Params: "up",
|
||||||
Options: option.NewActionOptions(opts...),
|
Options: option.NewActionOptions(opts...),
|
||||||
}
|
}
|
||||||
@@ -234,7 +234,7 @@ func (s *StepMobile) SwipeUp(opts ...option.ActionOption) *StepMobile {
|
|||||||
|
|
||||||
func (s *StepMobile) SwipeDown(opts ...option.ActionOption) *StepMobile {
|
func (s *StepMobile) SwipeDown(opts ...option.ActionOption) *StepMobile {
|
||||||
action := uixt.MobileAction{
|
action := uixt.MobileAction{
|
||||||
Method: option.ACTION_Swipe,
|
Method: option.ACTION_SwipeDirection,
|
||||||
Params: "down",
|
Params: "down",
|
||||||
Options: option.NewActionOptions(opts...),
|
Options: option.NewActionOptions(opts...),
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ func (s *StepMobile) SwipeDown(opts ...option.ActionOption) *StepMobile {
|
|||||||
|
|
||||||
func (s *StepMobile) SwipeLeft(opts ...option.ActionOption) *StepMobile {
|
func (s *StepMobile) SwipeLeft(opts ...option.ActionOption) *StepMobile {
|
||||||
action := uixt.MobileAction{
|
action := uixt.MobileAction{
|
||||||
Method: option.ACTION_Swipe,
|
Method: option.ACTION_SwipeDirection,
|
||||||
Params: "left",
|
Params: "left",
|
||||||
Options: option.NewActionOptions(opts...),
|
Options: option.NewActionOptions(opts...),
|
||||||
}
|
}
|
||||||
@@ -256,7 +256,7 @@ func (s *StepMobile) SwipeLeft(opts ...option.ActionOption) *StepMobile {
|
|||||||
|
|
||||||
func (s *StepMobile) SwipeRight(opts ...option.ActionOption) *StepMobile {
|
func (s *StepMobile) SwipeRight(opts ...option.ActionOption) *StepMobile {
|
||||||
action := uixt.MobileAction{
|
action := uixt.MobileAction{
|
||||||
Method: option.ACTION_Swipe,
|
Method: option.ACTION_SwipeDirection,
|
||||||
Params: "right",
|
Params: "right",
|
||||||
Options: option.NewActionOptions(opts...),
|
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")
|
Float64("toX", toX).Float64("toY", toY).Msg("ADBDriver.Swipe")
|
||||||
|
|
||||||
actionOptions := option.NewActionOptions(opts...)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer postHandler(ad, option.ACTION_Swipe, actionOptions)
|
defer postHandler(ad, option.ACTION_SwipeCoordinate, actionOptions)
|
||||||
|
|
||||||
// adb shell input swipe fromX fromY toX toY
|
// adb shell input swipe fromX fromY toX toY
|
||||||
_, err = ad.runShellCommand(
|
_, 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")
|
Float64("toX", toX).Float64("toY", toY).Msg("UIA2Driver.Swipe")
|
||||||
|
|
||||||
actionOptions := option.NewActionOptions(opts...)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer postHandler(ud, option.ACTION_Swipe, actionOptions)
|
defer postHandler(ud, option.ACTION_SwipeCoordinate, actionOptions)
|
||||||
|
|
||||||
duration := 200.0
|
duration := 200.0
|
||||||
if actionOptions.PressDuration > 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ func MarkUIOperation(driver IDriver, actionType option.ActionMethod, actionCoord
|
|||||||
x, y := actionCoordinates[0], actionCoordinates[1]
|
x, y := actionCoordinates[0], actionCoordinates[1]
|
||||||
point := image.Point{X: int(x), Y: int(y)}
|
point := image.Point{X: int(x), Y: int(y)}
|
||||||
err = SaveImageWithCircleMarker(compressedBufSource, point, imagePath)
|
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 {
|
if len(actionCoordinates) != 4 {
|
||||||
return fmt.Errorf("invalid swipe action coordinates: %v", actionCoordinates)
|
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
|
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 float64, err error) {
|
||||||
|
|
||||||
fromX, fromY, toX, toY, err = convertToAbsoluteCoordinates(driver, rawFomX, rawFromY, rawToX, rawToY)
|
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
|
// save screenshot before action and mark UI operation
|
||||||
if options.PreMarkOperation {
|
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")
|
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")
|
Float64("toX", toX).Float64("toY", toY).Msg("HDCDriver.Swipe")
|
||||||
|
|
||||||
actionOptions := option.NewActionOptions(opts...)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer postHandler(hd, option.ACTION_Swipe, actionOptions)
|
defer postHandler(hd, option.ACTION_SwipeCoordinate, actionOptions)
|
||||||
|
|
||||||
duration := 200
|
duration := 200
|
||||||
if actionOptions.PressDuration > 0 {
|
if actionOptions.PressDuration > 0 {
|
||||||
|
|||||||
+611
-212
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,8 @@ func TestNewMCPServer(t *testing.T) {
|
|||||||
"list_available_devices",
|
"list_available_devices",
|
||||||
"select_device",
|
"select_device",
|
||||||
"list_packages",
|
"list_packages",
|
||||||
"launch_app",
|
"app_launch",
|
||||||
"terminate_app",
|
"app_terminate",
|
||||||
"get_screen_size",
|
"get_screen_size",
|
||||||
"press_button",
|
"press_button",
|
||||||
"tap_xy",
|
"tap_xy",
|
||||||
@@ -54,7 +54,7 @@ func TestToolInterfaces(t *testing.T) {
|
|||||||
&ToolGetScreenSize{},
|
&ToolGetScreenSize{},
|
||||||
&ToolPressButton{},
|
&ToolPressButton{},
|
||||||
&ToolTapXY{},
|
&ToolTapXY{},
|
||||||
&ToolSwipe{},
|
&ToolSwipeDirection{},
|
||||||
&ToolDrag{},
|
&ToolDrag{},
|
||||||
&ToolScreenShot{},
|
&ToolScreenShot{},
|
||||||
&ToolHome{},
|
&ToolHome{},
|
||||||
@@ -64,7 +64,7 @@ func TestToolInterfaces(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tool := range tools {
|
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.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.Options(), "Tool options should not be nil")
|
||||||
assert.NotNil(t, tool.Implement(), "Tool implementation should not be nil")
|
assert.NotNil(t, tool.Implement(), "Tool implementation should not be nil")
|
||||||
|
|||||||
+14
-6
@@ -12,15 +12,17 @@ type ActionMethod string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ACTION_LOG ActionMethod = "log"
|
ACTION_LOG ActionMethod = "log"
|
||||||
ACTION_AppInstall ActionMethod = "install"
|
ACTION_ListPackages ActionMethod = "list_packages"
|
||||||
ACTION_AppUninstall ActionMethod = "uninstall"
|
ACTION_AppInstall ActionMethod = "app_install"
|
||||||
ACTION_WebLoginNoneUI ActionMethod = "login_none_ui"
|
ACTION_AppUninstall ActionMethod = "app_uninstall"
|
||||||
|
ACTION_WebLoginNoneUI ActionMethod = "web_login_none_ui"
|
||||||
ACTION_AppClear ActionMethod = "app_clear"
|
ACTION_AppClear ActionMethod = "app_clear"
|
||||||
ACTION_AppStart ActionMethod = "app_start"
|
ACTION_AppStart ActionMethod = "app_start"
|
||||||
ACTION_AppLaunch ActionMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
|
ACTION_AppLaunch ActionMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
|
||||||
ACTION_AppTerminate ActionMethod = "app_terminate"
|
ACTION_AppTerminate ActionMethod = "app_terminate"
|
||||||
ACTION_AppStop ActionMethod = "app_stop"
|
ACTION_AppStop ActionMethod = "app_stop"
|
||||||
ACTION_ScreenShot ActionMethod = "screenshot"
|
ACTION_ScreenShot ActionMethod = "screenshot"
|
||||||
|
ACTION_GetScreenSize ActionMethod = "get_screen_size"
|
||||||
ACTION_Sleep ActionMethod = "sleep"
|
ACTION_Sleep ActionMethod = "sleep"
|
||||||
ACTION_SleepMS ActionMethod = "sleep_ms"
|
ACTION_SleepMS ActionMethod = "sleep_ms"
|
||||||
ACTION_SleepRandom ActionMethod = "sleep_random"
|
ACTION_SleepRandom ActionMethod = "sleep_random"
|
||||||
@@ -33,12 +35,14 @@ const (
|
|||||||
ACTION_Home ActionMethod = "home"
|
ACTION_Home ActionMethod = "home"
|
||||||
ACTION_TapXY ActionMethod = "tap_xy"
|
ACTION_TapXY ActionMethod = "tap_xy"
|
||||||
ACTION_TapAbsXY ActionMethod = "tap_abs_xy"
|
ACTION_TapAbsXY ActionMethod = "tap_abs_xy"
|
||||||
ACTION_TapByOCR ActionMethod = "tap_ocr"
|
ACTION_TapByOCR ActionMethod = "tap_by_ocr"
|
||||||
ACTION_TapByCV ActionMethod = "tap_cv"
|
ACTION_TapByCV ActionMethod = "tap_by_cv"
|
||||||
ACTION_DoubleTapXY ActionMethod = "double_tap_xy"
|
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_Drag ActionMethod = "drag"
|
||||||
ACTION_Input ActionMethod = "input"
|
ACTION_Input ActionMethod = "input"
|
||||||
|
ACTION_PressButton ActionMethod = "press_button"
|
||||||
ACTION_Back ActionMethod = "back"
|
ACTION_Back ActionMethod = "back"
|
||||||
ACTION_KeyCode ActionMethod = "keycode"
|
ACTION_KeyCode ActionMethod = "keycode"
|
||||||
ACTION_AIAction ActionMethod = "ai_action" // action with ai
|
ACTION_AIAction ActionMethod = "ai_action" // action with ai
|
||||||
@@ -49,6 +53,10 @@ const (
|
|||||||
ACTION_SecondaryClickBySelector ActionMethod = "secondary_click_by_selector"
|
ACTION_SecondaryClickBySelector ActionMethod = "secondary_click_by_selector"
|
||||||
ACTION_GetElementTextBySelector ActionMethod = "get_element_text_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
|
// custom actions
|
||||||
ACTION_SwipeToTapApp ActionMethod = "swipe_to_tap_app" // swipe left & right to find app and tap
|
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
|
ACTION_SwipeToTapText ActionMethod = "swipe_to_tap_text" // swipe up & down to find text and tap
|
||||||
|
|||||||
+18
-739
@@ -2,10 +2,8 @@ package uixt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/internal/builtin"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/ai"
|
"github.com/httprunner/httprunner/v5/uixt/ai"
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||||
"github.com/mark3labs/mcp-go/client"
|
"github.com/mark3labs/mcp-go/client"
|
||||||
@@ -16,7 +14,7 @@ import (
|
|||||||
func NewXTDriver(driver IDriver, opts ...option.AIServiceOption) (*XTDriver, error) {
|
func NewXTDriver(driver IDriver, opts ...option.AIServiceOption) (*XTDriver, error) {
|
||||||
driverExt := &XTDriver{
|
driverExt := &XTDriver{
|
||||||
IDriver: driver,
|
IDriver: driver,
|
||||||
client: &MCPClient4XTDriver{
|
Client: &MCPClient4XTDriver{
|
||||||
Server: NewMCPServer(),
|
Server: NewMCPServer(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -48,7 +46,7 @@ type XTDriver struct {
|
|||||||
CVService ai.ICVService // OCR/CV
|
CVService ai.ICVService // OCR/CV
|
||||||
LLMService ai.ILLMService // LLM
|
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
|
// 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) {
|
func (c *MCPClient4XTDriver) CallTool(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
handler := c.Server.GetHandler(req.Params.Name)
|
actionTool := c.Server.GetToolByActionMethod(option.ActionMethod(req.Params.Name))
|
||||||
if handler == nil {
|
if actionTool == nil {
|
||||||
return mcp.NewToolResultError(fmt.Sprintf("handler for tool %s not found", req.Params.Name)), nil
|
return mcp.NewToolResultError(fmt.Sprintf("action %s for tool not found", req.Params.Name)), nil
|
||||||
}
|
}
|
||||||
|
handler := actionTool.Implement()
|
||||||
return handler(ctx, req)
|
return handler(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,14 +80,20 @@ func (c *MCPClient4XTDriver) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dExt *XTDriver) ExecuteAction(action MobileAction) (err error) {
|
func (dExt *XTDriver) ExecuteAction(action MobileAction) (err error) {
|
||||||
// Convert action to MCP tool call
|
// Find the corresponding tool for this action method
|
||||||
req, err := convertActionToCallToolRequest(action)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to convert action to MCP tool call: %w", err)
|
return fmt.Errorf("failed to convert action to MCP tool call: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute via MCP tool
|
// Execute via MCP tool
|
||||||
result, err := dExt.client.CallTool(context.Background(), req)
|
result, err := dExt.Client.CallTool(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("MCP tool call failed: %w", err)
|
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")
|
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
|
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