refactor: merge DoAction to mcp server tools

This commit is contained in:
lilong.129
2025-05-25 23:53:07 +08:00
parent 7986c4899f
commit 2e17d9df16
36 changed files with 725 additions and 1006 deletions
+2 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+31
View 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
+5 -5
View File
@@ -13,10 +13,10 @@ hrp mcphost [flags]
### Options ### Options
``` ```
--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
+1 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -1 +1 @@
v5.0.0-beta-2505250810 v5.0.0-beta-2505252353
+5 -5
View File
@@ -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...),
} }
+2 -2
View File
@@ -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(
+2 -2
View File
@@ -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 {
+1 -1
View File
@@ -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
} }
+1 -1
View File
@@ -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)
} }
+3 -2
View File
@@ -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")
} }
} }
+2 -2
View File
@@ -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 {
+613 -214
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}