feat: 重构 MCP 工具导出逻辑并完善返回值类型系统

This commit is contained in:
lilong.129
2025-05-31 00:28:24 +08:00
parent 2a392f204b
commit 9089bd9324
7 changed files with 924 additions and 52 deletions

View File

@@ -1 +1 @@
v5.0.0-beta-2505300045
v5.0.0-beta-2505310028

View File

@@ -9,20 +9,30 @@ import (
"time"
"github.com/bytedance/sonic"
"github.com/mark3labs/mcp-go/mcp"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/uixt"
"github.com/httprunner/httprunner/v5/uixt/option"
)
// MCPToolRecord represents a single tool record in the database
// Each record contains detailed information about a tool and its server
type MCPToolRecord struct {
ToolID string `json:"tool_id"` // Unique identifier for the tool record
ServerName string `json:"mcp_server"` // Name of the MCP server
ToolName string `json:"tool_name"` // Name of the tool
Description string `json:"description"` // Tool description
Parameters string `json:"parameters"` // Tool input parameters in JSON format
Returns string `json:"returns"` // Tool return value format in JSON format
CreatedAt time.Time `json:"created_at"` // Record creation time
LastUpdatedAt time.Time `json:"last_updated_at"` // Record last update time
ToolID string `json:"tool_id"` // Unique identifier for the tool record
BizID string `json:"biz_id"` // Business ID of the tool
VisibleRange int `json:"visible_range"` // Visible range of the tool, 0: visible to biz, 1: visible to all
ToolType string `json:"tool_type"` // Type of the tool
ServerName string `json:"mcp_server"` // Name of the MCP server
ToolName string `json:"tool_name"` // Name of the tool
Description string `json:"description"` // Tool description
Parameters string `json:"parameters"` // Tool input parameters in JSON format
Returns string `json:"return_desc"` // Tool return value format in JSON format
TeardownPair string `json:"teardown_pair"` // Teardown pair of the tool
Examples string `json:"examples"` // Examples of the tool
SupportPatterns string `json:"support_patterns"` // Support pattern of the tool
CreatedAt time.Time `json:"created_at"` // Record creation time
LastUpdatedAt time.Time `json:"last_updated_at"` // Record last update time
}
// DocStringInfo contains the parsed information from a Python docstring
@@ -109,8 +119,13 @@ func extractDocStringInfo(docstring string) DocStringInfo {
return info
}
// ActionToolProvider defines the interface for MCP servers that provide ActionTool implementations
type ActionToolProvider interface {
GetToolByAction(actionName option.ActionName) uixt.ActionTool
}
// ConvertToolsToRecords converts []MCPTools to a list of database records
func ConvertToolsToRecords(tools []MCPTools) []MCPToolRecord {
func (host *MCPHost) ConvertToolsToRecords(tools []MCPTools) []MCPToolRecord {
var records []MCPToolRecord
now := time.Now()
@@ -121,36 +136,7 @@ func ConvertToolsToRecords(tools []MCPTools) []MCPToolRecord {
}
for _, tool := range mcpTools.Tools {
// Generate unique ID by combining server name and tool name
id := fmt.Sprintf("%s__%s", mcpTools.ServerName, tool.Name)
// Extract docstring information
info := extractDocStringInfo(tool.Description)
// Convert parameters and returns to JSON
paramsJSON, err := sonic.MarshalString(info.Parameters)
if err != nil {
log.Warn().Interface("params", info.Parameters).Err(err).Msg("failed to marshal parameters to JSON")
paramsJSON = "{}"
}
returnsJSON, err := sonic.MarshalString(info.Returns)
if err != nil {
log.Warn().Interface("returns", info.Returns).Err(err).Msg("failed to marshal returns to JSON")
returnsJSON = "{}"
}
record := MCPToolRecord{
ToolID: id,
ServerName: mcpTools.ServerName,
ToolName: tool.Name,
Description: info.Description,
Parameters: paramsJSON,
Returns: returnsJSON,
CreatedAt: now,
LastUpdatedAt: now,
}
record := host.convertSingleToolToRecord(mcpTools.ServerName, tool, now)
records = append(records, record)
}
}
@@ -158,12 +144,121 @@ func ConvertToolsToRecords(tools []MCPTools) []MCPToolRecord {
return records
}
// convertSingleToolToRecord converts a single MCP tool to a database record
func (host *MCPHost) convertSingleToolToRecord(serverName string, tool mcp.Tool, timestamp time.Time) MCPToolRecord {
// Generate unique ID
id := fmt.Sprintf("%s__%s", serverName, tool.Name)
// Extract description from docstring
info := extractDocStringInfo(tool.Description)
// Extract parameters
paramsJSON := host.extractParameters(tool, info)
// Extract returns
returnsJSON := host.extractReturns(serverName, tool.Name, info)
return MCPToolRecord{
ToolID: id,
VisibleRange: 1,
ToolType: "edge",
ServerName: serverName,
ToolName: tool.Name,
Description: info.Description,
Parameters: paramsJSON,
Returns: returnsJSON,
CreatedAt: timestamp,
LastUpdatedAt: timestamp,
}
}
// extractParameters extracts parameter information from tool schema or docstring
func (host *MCPHost) extractParameters(tool mcp.Tool, info DocStringInfo) string {
// Priority 1: Extract from InputSchema.Properties
if len(tool.InputSchema.Properties) > 0 {
return host.extractParametersFromSchema(tool.InputSchema.Properties)
}
// Priority 2: Extract from docstring
if len(info.Parameters) > 0 {
return host.marshalToJSON(info.Parameters, "docstring parameters")
}
return "{}"
}
// extractParametersFromSchema extracts parameters from MCP tool input schema
func (host *MCPHost) extractParametersFromSchema(properties map[string]interface{}) string {
schemaParams := make(map[string]string)
for propName, propValue := range properties {
propMap, ok := propValue.(map[string]interface{})
if !ok {
continue
}
description := host.getPropertyDescription(propMap)
schemaParams[propName] = description
}
return host.marshalToJSON(schemaParams, "schema parameters")
}
// getPropertyDescription extracts description from property map
func (host *MCPHost) getPropertyDescription(propMap map[string]interface{}) string {
if desc, exists := propMap["description"]; exists {
if descStr, ok := desc.(string); ok {
return descStr
}
}
// Fallback to type information
if propType, exists := propMap["type"]; exists {
if typeStr, ok := propType.(string); ok {
return fmt.Sprintf("Parameter of type %s", typeStr)
}
}
return "Parameter"
}
// extractReturns extracts return value information from ActionTool or docstring
func (host *MCPHost) extractReturns(serverName, toolName string, info DocStringInfo) string {
// Priority 1: Get from ActionTool interface if available
if actionToolProvider := host.getActionToolProvider(serverName); actionToolProvider != nil {
if actionTool := actionToolProvider.GetToolByAction(option.ActionName(toolName)); actionTool != nil {
returnSchema := actionTool.ReturnSchema()
if len(returnSchema) > 0 {
return host.marshalToJSON(returnSchema, "return schema")
}
}
}
// Priority 2: Use docstring returns as fallback
if len(info.Returns) > 0 {
return host.marshalToJSON(info.Returns, "docstring returns")
}
return "{}"
}
// marshalToJSON marshals data to JSON string with error handling
func (host *MCPHost) marshalToJSON(data interface{}, dataType string) string {
jsonBytes, err := sonic.MarshalString(data)
if err != nil {
log.Warn().Interface("data", data).Err(err).
Msgf("failed to marshal %s to JSON", dataType)
return "{}"
}
return jsonBytes
}
// ExportToolsToJSON dumps MCP tools to JSON file
func (h *MCPHost) ExportToolsToJSON(ctx context.Context, dumpPath string) error {
// get all tools
tools := h.GetTools(ctx)
// convert to records
records := ConvertToolsToRecords(tools)
records := h.ConvertToolsToRecords(tools)
// convert to JSON
recordsJSON, err := sonic.MarshalIndent(records, "", " ")
if err != nil {

View File

@@ -124,6 +124,11 @@ func TestExtractDocStringInfo(t *testing.T) {
}
func TestConvertToolsToRecords(t *testing.T) {
// Create a mock MCPHost for testing
host := &MCPHost{
connections: make(map[string]*Connection),
}
tests := []struct {
name string
tools []MCPTools
@@ -152,7 +157,7 @@ func TestConvertToolsToRecords(t *testing.T) {
},
want: []MCPToolRecord{
{
ToolID: "weather_get_alerts",
ToolID: "weather__get_alerts",
ServerName: "weather",
ToolName: "get_alerts",
Description: "Get weather alerts for a US state.",
@@ -184,7 +189,7 @@ func TestConvertToolsToRecords(t *testing.T) {
},
want: []MCPToolRecord{
{
ToolID: "ui_swipe",
ToolID: "ui__swipe",
ServerName: "ui",
ToolName: "swipe",
Description: "Do screen swipe action.",
@@ -192,7 +197,7 @@ func TestConvertToolsToRecords(t *testing.T) {
Returns: "{}",
},
{
ToolID: "ui_tap",
ToolID: "ui__tap",
ServerName: "ui",
ToolName: "tap",
Description: "Tap on screen at specified position.",
@@ -201,11 +206,47 @@ func TestConvertToolsToRecords(t *testing.T) {
},
},
},
{
name: "convert tool with InputSchema",
tools: []MCPTools{
{
ServerName: "test",
Tools: []mcp.Tool{
{
Name: "test_tool",
Description: "Test tool with input schema",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]interface{}{
"param1": map[string]interface{}{
"type": "string",
"description": "First parameter",
},
"param2": map[string]interface{}{
"type": "number",
},
},
},
},
},
},
},
want: []MCPToolRecord{
{
ToolID: "test__test_tool",
ServerName: "test",
ToolName: "test_tool",
Description: "Test tool with input schema",
Parameters: `{"param1":"First parameter","param2":"Parameter of type number"}`,
Returns: "{}",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertToolsToRecords(tt.tools)
got := host.ConvertToolsToRecords(tt.tools)
// Compare each record
require.Equal(t, len(tt.want), len(got))
@@ -235,3 +276,179 @@ func TestConvertToolsToRecords(t *testing.T) {
})
}
}
// TestExtractParameters tests the extractParameters method
func TestExtractParameters(t *testing.T) {
host := &MCPHost{}
tests := []struct {
name string
tool mcp.Tool
info DocStringInfo
expected string
}{
{
name: "extract from InputSchema",
tool: mcp.Tool{
InputSchema: mcp.ToolInputSchema{
Properties: map[string]interface{}{
"param1": map[string]interface{}{
"type": "string",
"description": "First parameter",
},
"param2": map[string]interface{}{
"type": "number",
},
},
},
},
info: DocStringInfo{Parameters: map[string]string{"old": "old param"}},
expected: `{"param1":"First parameter","param2":"Parameter of type number"}`,
},
{
name: "fallback to docstring",
tool: mcp.Tool{},
info: DocStringInfo{
Parameters: map[string]string{
"param": "parameter description",
},
},
expected: `{"param":"parameter description"}`,
},
{
name: "empty parameters",
tool: mcp.Tool{},
info: DocStringInfo{},
expected: "{}",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := host.extractParameters(tt.tool, tt.info)
assert.Equal(t, tt.expected, got)
})
}
}
// TestExtractReturns tests the extractReturns method
func TestExtractReturns(t *testing.T) {
host := &MCPHost{
connections: make(map[string]*Connection),
}
tests := []struct {
name string
serverName string
toolName string
info DocStringInfo
expected string
}{
{
name: "fallback to docstring returns",
serverName: "unknown_server",
toolName: "unknown_tool",
info: DocStringInfo{
Returns: map[string]string{
"result": "operation result",
"error": "error message",
},
},
expected: `{"error":"error message","result":"operation result"}`,
},
{
name: "empty returns",
serverName: "unknown_server",
toolName: "unknown_tool",
info: DocStringInfo{},
expected: "{}",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := host.extractReturns(tt.serverName, tt.toolName, tt.info)
assert.Equal(t, tt.expected, got)
})
}
}
// TestGetPropertyDescription tests the getPropertyDescription method
func TestGetPropertyDescription(t *testing.T) {
host := &MCPHost{}
tests := []struct {
name string
propMap map[string]interface{}
expected string
}{
{
name: "with description",
propMap: map[string]interface{}{
"type": "string",
"description": "Parameter description",
},
expected: "Parameter description",
},
{
name: "without description, with type",
propMap: map[string]interface{}{
"type": "number",
},
expected: "Parameter of type number",
},
{
name: "without description and type",
propMap: map[string]interface{}{},
expected: "Parameter",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := host.getPropertyDescription(tt.propMap)
assert.Equal(t, tt.expected, got)
})
}
}
// TestMarshalToJSON tests the marshalToJSON method
func TestMarshalToJSON(t *testing.T) {
host := &MCPHost{}
tests := []struct {
name string
data interface{}
dataType string
expected string
}{
{
name: "valid map",
data: map[string]string{
"key1": "value1",
"key2": "value2",
},
dataType: "test data",
expected: `{"key1":"value1","key2":"value2"}`,
},
{
name: "empty map",
data: map[string]string{},
dataType: "test data",
expected: "{}",
},
{
name: "invalid data (channel)",
data: make(chan int),
dataType: "test data",
expected: "{}",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := host.marshalToJSON(tt.data, tt.dataType)
assert.Equal(t, tt.expected, got)
})
}
}

View File

@@ -558,3 +558,18 @@ func (h *MCPHost) forceCloseAll() {
delete(h.connections, name)
}
}
// getActionToolProvider returns an ActionToolProvider for the given server name if available
// This method checks if the MCP server implements the ActionToolProvider interface
func (h *MCPHost) getActionToolProvider(serverName string) ActionToolProvider {
h.mu.RLock()
defer h.mu.RUnlock()
if conn, exists := h.connections[serverName]; exists {
// Check if the client directly implements ActionToolProvider interface
if actionToolProvider, ok := conn.Client.(ActionToolProvider); ok {
return actionToolProvider
}
}
return nil
}

View File

@@ -304,7 +304,7 @@ func (s *MCPServer4XTDriver) registerTools() {
s.registerTool(&ToolTapByCV{}) // tap by CV
s.registerTool(&ToolDoubleTapXY{}) // double tap xy
// Swipe Tool
// Swipe Tools
s.registerTool(&ToolSwipe{}) // generic swipe, auto-detect direction or coordinate
s.registerTool(&ToolSwipeDirection{}) // swipe direction, up/down/left/right
s.registerTool(&ToolSwipeCoordinate{}) // swipe coordinate, [fromX, fromY, toX, toY]
@@ -337,7 +337,7 @@ func (s *MCPServer4XTDriver) registerTools() {
s.registerTool(&ToolAppUninstall{}) // AppUninstall
s.registerTool(&ToolAppClear{}) // AppClear
// Sleep Tool
// Sleep Tools
s.registerTool(&ToolSleep{})
s.registerTool(&ToolSleepMS{})
s.registerTool(&ToolSleepRandom{})
@@ -432,6 +432,8 @@ type ActionTool interface {
Implement() server.ToolHandlerFunc
// ConvertActionToCallToolRequest converts MobileAction to mcp.CallToolRequest
ConvertActionToCallToolRequest(action MobileAction) (mcp.CallToolRequest, error)
// ReturnSchema returns the expected return value schema based on mcp.CallToolResult conventions
ReturnSchema() map[string]string
}
// buildMCPCallToolRequest is a helper function to build mcp.CallToolRequest
@@ -505,6 +507,13 @@ func (t *ToolListAvailableDevices) ConvertActionToCallToolRequest(action MobileA
return buildMCPCallToolRequest(t.Name(), map[string]any{}), nil
}
func (t *ToolListAvailableDevices) ReturnSchema() map[string]string {
return map[string]string{
"androidDevices": "[]string: List of Android device serial numbers",
"iosDevices": "[]string: List of iOS device UDIDs",
}
}
// ToolSelectDevice implements the select_device tool call.
type ToolSelectDevice struct{}
@@ -539,6 +548,12 @@ func (t *ToolSelectDevice) ConvertActionToCallToolRequest(action MobileAction) (
return buildMCPCallToolRequest(t.Name(), map[string]any{}), nil
}
func (t *ToolSelectDevice) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message with selected device UUID",
}
}
// ToolTapXY implements the tap_xy tool call.
//
// This tool performs touch/click operations at specified relative coordinates on the device screen.
@@ -656,6 +671,12 @@ func (t *ToolTapXY) ConvertActionToCallToolRequest(action MobileAction) (mcp.Cal
return mcp.CallToolRequest{}, fmt.Errorf("invalid tap params: %v", action.Params)
}
func (t *ToolTapXY) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming tap operation at specified coordinates",
}
}
// ToolTapAbsXY implements the tap_abs_xy tool call.
type ToolTapAbsXY struct{}
@@ -734,6 +755,19 @@ func (t *ToolTapAbsXY) ConvertActionToCallToolRequest(action MobileAction) (mcp.
return mcp.CallToolRequest{}, fmt.Errorf("invalid tap abs params: %v", action.Params)
}
func (t *ToolTapAbsXY) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming tap operation at absolute coordinates",
}
}
// defaultReturnSchema provides a standard return schema for most tools
func defaultReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming the operation was completed",
}
}
// ToolTapByOCR implements the tap_ocr tool call.
type ToolTapByOCR struct{}
@@ -800,6 +834,12 @@ func (t *ToolTapByOCR) ConvertActionToCallToolRequest(action MobileAction) (mcp.
return mcp.CallToolRequest{}, fmt.Errorf("invalid tap by OCR params: %v", action.Params)
}
func (t *ToolTapByOCR) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming the operation was completed",
}
}
// ToolTapByCV implements the tap_cv tool call.
type ToolTapByCV struct{}
@@ -863,6 +903,10 @@ func (t *ToolTapByCV) ConvertActionToCallToolRequest(action MobileAction) (mcp.C
return buildMCPCallToolRequest(t.Name(), arguments), nil
}
func (t *ToolTapByCV) ReturnSchema() map[string]string {
return defaultReturnSchema()
}
// ToolDoubleTapXY implements the double_tap_xy tool call.
type ToolDoubleTapXY struct{}
@@ -919,6 +963,10 @@ func (t *ToolDoubleTapXY) ConvertActionToCallToolRequest(action MobileAction) (m
return mcp.CallToolRequest{}, fmt.Errorf("invalid double tap params: %v", action.Params)
}
func (t *ToolDoubleTapXY) ReturnSchema() map[string]string {
return defaultReturnSchema()
}
// ToolListPackages implements the list_packages tool call.
type ToolListPackages struct{}
@@ -954,6 +1002,12 @@ func (t *ToolListPackages) ConvertActionToCallToolRequest(action MobileAction) (
return buildMCPCallToolRequest(t.Name(), map[string]any{}), nil
}
func (t *ToolListPackages) ReturnSchema() map[string]string {
return map[string]string{
"packages": "[]string: List of installed app package names on the device",
}
}
// ToolLaunchApp implements the launch_app tool call.
type ToolLaunchApp struct{}
@@ -1007,6 +1061,10 @@ func (t *ToolLaunchApp) ConvertActionToCallToolRequest(action MobileAction) (mcp
return mcp.CallToolRequest{}, fmt.Errorf("invalid app launch params: %v", action.Params)
}
func (t *ToolLaunchApp) ReturnSchema() map[string]string {
return defaultReturnSchema()
}
// ToolTerminateApp implements the terminate_app tool call.
type ToolTerminateApp struct{}
@@ -1063,6 +1121,10 @@ func (t *ToolTerminateApp) ConvertActionToCallToolRequest(action MobileAction) (
return mcp.CallToolRequest{}, fmt.Errorf("invalid app terminate params: %v", action.Params)
}
func (t *ToolTerminateApp) ReturnSchema() map[string]string {
return defaultReturnSchema()
}
// ToolScreenShot implements the screenshot tool call.
type ToolScreenShot struct{}
@@ -1100,6 +1162,14 @@ func (t *ToolScreenShot) ConvertActionToCallToolRequest(action MobileAction) (mc
return buildMCPCallToolRequest(t.Name(), map[string]any{}), nil
}
func (t *ToolScreenShot) ReturnSchema() map[string]string {
return map[string]string{
"image": "string: Base64 encoded screenshot image in JPEG format",
"name": "string: Image name identifier (typically 'screenshot')",
"type": "string: MIME type of the image (image/jpeg)",
}
}
// ToolGetScreenSize implements the get_screen_size tool call.
type ToolGetScreenSize struct{}
@@ -1137,6 +1207,14 @@ func (t *ToolGetScreenSize) ConvertActionToCallToolRequest(action MobileAction)
return buildMCPCallToolRequest(t.Name(), map[string]any{}), nil
}
func (t *ToolGetScreenSize) ReturnSchema() map[string]string {
return map[string]string{
"width": "int: Screen width in pixels",
"height": "int: Screen height in pixels",
"message": "string: Formatted message with screen dimensions",
}
}
// ToolPressButton implements the press_button tool call.
type ToolPressButton struct{}
@@ -1186,6 +1264,13 @@ func (t *ToolPressButton) ConvertActionToCallToolRequest(action MobileAction) (m
return mcp.CallToolRequest{}, fmt.Errorf("invalid press button params: %v", action.Params)
}
func (t *ToolPressButton) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming the button press operation",
"button": "string: Name of the button that was pressed",
}
}
// ToolSwipe implements the generic swipe tool call.
// It automatically determines whether to use direction-based or coordinate-based swipe
// based on the params type.
@@ -1249,6 +1334,17 @@ func (t *ToolSwipe) ConvertActionToCallToolRequest(action MobileAction) (mcp.Cal
return mcp.CallToolRequest{}, fmt.Errorf("invalid swipe params: %v, expected string direction or [fromX, fromY, toX, toY] coordinates", action.Params)
}
func (t *ToolSwipe) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming the swipe operation",
"direction": "string: Direction of swipe (for directional swipes)",
"fromX": "float64: Starting X coordinate (for coordinate-based swipes)",
"fromY": "float64: Starting Y coordinate (for coordinate-based swipes)",
"toX": "float64: Ending X coordinate (for coordinate-based swipes)",
"toY": "float64: Ending Y coordinate (for coordinate-based swipes)",
}
}
// ToolSwipeDirection implements the swipe_direction tool call.
type ToolSwipeDirection struct{}
@@ -1344,6 +1440,13 @@ func (t *ToolSwipeDirection) ConvertActionToCallToolRequest(action MobileAction)
return mcp.CallToolRequest{}, fmt.Errorf("invalid swipe params: %v", action.Params)
}
func (t *ToolSwipeDirection) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming the directional swipe",
"direction": "string: Direction that was swiped (up/down/left/right)",
}
}
// ToolSwipeCoordinate implements the swipe_coordinate tool call.
type ToolSwipeCoordinate struct{}
@@ -1432,6 +1535,16 @@ func (t *ToolSwipeCoordinate) ConvertActionToCallToolRequest(action MobileAction
return mcp.CallToolRequest{}, fmt.Errorf("invalid swipe advanced params: %v", action.Params)
}
func (t *ToolSwipeCoordinate) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming the coordinate-based swipe",
"fromX": "float64: Starting X coordinate of the swipe",
"fromY": "float64: Starting Y coordinate of the swipe",
"toX": "float64: Ending X coordinate of the swipe",
"toY": "float64: Ending Y coordinate of the swipe",
}
}
// ToolSwipeToTapApp implements the swipe_to_tap_app tool call.
type ToolSwipeToTapApp struct{}
@@ -1501,6 +1614,13 @@ func (t *ToolSwipeToTapApp) ConvertActionToCallToolRequest(action MobileAction)
return mcp.CallToolRequest{}, fmt.Errorf("invalid swipe to tap app params: %v", action.Params)
}
func (t *ToolSwipeToTapApp) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming the app was found and tapped",
"appName": "string: Name of the app that was found and tapped",
}
}
// ToolSwipeToTapText implements the swipe_to_tap_text tool call.
type ToolSwipeToTapText struct{}
@@ -1573,6 +1693,13 @@ func (t *ToolSwipeToTapText) ConvertActionToCallToolRequest(action MobileAction)
return mcp.CallToolRequest{}, fmt.Errorf("invalid swipe to tap text params: %v", action.Params)
}
func (t *ToolSwipeToTapText) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming the text was found and tapped",
"text": "string: Text content that was found and tapped",
}
}
// ToolSwipeToTapTexts implements the swipe_to_tap_texts tool call.
type ToolSwipeToTapTexts struct{}
@@ -1650,6 +1777,14 @@ func (t *ToolSwipeToTapTexts) ConvertActionToCallToolRequest(action MobileAction
return buildMCPCallToolRequest(t.Name(), arguments), nil
}
func (t *ToolSwipeToTapTexts) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming one of the texts was found and tapped",
"texts": "[]string: List of text options that were searched for",
"foundText": "string: The specific text that was actually found and tapped",
}
}
// ToolDrag implements the drag tool call.
type ToolDrag struct{}
@@ -1732,6 +1867,16 @@ func (t *ToolDrag) ConvertActionToCallToolRequest(action MobileAction) (mcp.Call
return mcp.CallToolRequest{}, fmt.Errorf("invalid drag parameters: %v", action.Params)
}
func (t *ToolDrag) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming the drag operation",
"fromX": "float64: Starting X coordinate of the drag",
"fromY": "float64: Starting Y coordinate of the drag",
"toX": "float64: Ending X coordinate of the drag",
"toY": "float64: Ending Y coordinate of the drag",
}
}
// extractActionOptionsToArguments extracts action options and adds them to arguments map
// This is a generic helper that can be used by multiple tools
func extractActionOptionsToArguments(actionOptions []option.ActionOption, arguments map[string]any) {
@@ -1817,6 +1962,12 @@ func (t *ToolHome) ConvertActionToCallToolRequest(action MobileAction) (mcp.Call
return buildMCPCallToolRequest(t.Name(), map[string]any{}), nil
}
func (t *ToolHome) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming home button was pressed",
}
}
// ToolBack implements the back tool call.
type ToolBack struct{}
@@ -1855,6 +2006,12 @@ func (t *ToolBack) ConvertActionToCallToolRequest(action MobileAction) (mcp.Call
return buildMCPCallToolRequest(t.Name(), map[string]any{}), nil
}
func (t *ToolBack) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming back button was pressed",
}
}
// ToolInput implements the input tool call.
type ToolInput struct{}
@@ -1906,6 +2063,13 @@ func (t *ToolInput) ConvertActionToCallToolRequest(action MobileAction) (mcp.Cal
return buildMCPCallToolRequest(t.Name(), arguments), nil
}
func (t *ToolInput) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming text was input",
"text": "string: Text content that was input into the field",
}
}
// ToolWebLoginNoneUI implements the web_login_none_ui tool call.
type ToolWebLoginNoneUI struct{}
@@ -1954,6 +2118,13 @@ func (t *ToolWebLoginNoneUI) ConvertActionToCallToolRequest(action MobileAction)
return buildMCPCallToolRequest(t.Name(), map[string]any{}), nil
}
func (t *ToolWebLoginNoneUI) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming web login was completed",
"loginResult": "object: Result of the login operation (success/failure details)",
}
}
// ToolAppInstall implements the app_install tool call.
type ToolAppInstall struct{}
@@ -2003,6 +2174,13 @@ func (t *ToolAppInstall) ConvertActionToCallToolRequest(action MobileAction) (mc
return mcp.CallToolRequest{}, fmt.Errorf("invalid app install params: %v", action.Params)
}
func (t *ToolAppInstall) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming app installation",
"appUrl": "string: URL or path of the app that was installed",
}
}
// ToolAppUninstall implements the app_uninstall tool call.
type ToolAppUninstall struct{}
@@ -2052,6 +2230,13 @@ func (t *ToolAppUninstall) ConvertActionToCallToolRequest(action MobileAction) (
return mcp.CallToolRequest{}, fmt.Errorf("invalid app uninstall params: %v", action.Params)
}
func (t *ToolAppUninstall) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming app uninstallation",
"packageName": "string: Package name of the app that was uninstalled",
}
}
// ToolAppClear implements the app_clear tool call.
type ToolAppClear struct{}
@@ -2101,6 +2286,13 @@ func (t *ToolAppClear) ConvertActionToCallToolRequest(action MobileAction) (mcp.
return mcp.CallToolRequest{}, fmt.Errorf("invalid app clear params: %v", action.Params)
}
func (t *ToolAppClear) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming app data and cache were cleared",
"packageName": "string: Package name of the app that was cleared",
}
}
// ToolSecondaryClick implements the secondary_click tool call.
type ToolSecondaryClick struct{}
@@ -2156,6 +2348,14 @@ func (t *ToolSecondaryClick) ConvertActionToCallToolRequest(action MobileAction)
return mcp.CallToolRequest{}, fmt.Errorf("invalid secondary click params: %v", action.Params)
}
func (t *ToolSecondaryClick) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming secondary click (right-click) operation",
"x": "float64: X coordinate where secondary click was performed",
"y": "float64: Y coordinate where secondary click was performed",
}
}
// ToolHoverBySelector implements the hover_by_selector tool call.
type ToolHoverBySelector struct{}
@@ -2205,6 +2405,13 @@ func (t *ToolHoverBySelector) ConvertActionToCallToolRequest(action MobileAction
return mcp.CallToolRequest{}, fmt.Errorf("invalid hover by selector params: %v", action.Params)
}
func (t *ToolHoverBySelector) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming hover operation",
"selector": "string: CSS selector or XPath of the element that was hovered over",
}
}
// ToolTapBySelector implements the tap_by_selector tool call.
type ToolTapBySelector struct{}
@@ -2254,6 +2461,13 @@ func (t *ToolTapBySelector) ConvertActionToCallToolRequest(action MobileAction)
return mcp.CallToolRequest{}, fmt.Errorf("invalid tap by selector params: %v", action.Params)
}
func (t *ToolTapBySelector) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming tap operation",
"selector": "string: CSS selector or XPath of the element that was tapped",
}
}
// ToolSecondaryClickBySelector implements the secondary_click_by_selector tool call.
type ToolSecondaryClickBySelector struct{}
@@ -2303,6 +2517,13 @@ func (t *ToolSecondaryClickBySelector) ConvertActionToCallToolRequest(action Mob
return mcp.CallToolRequest{}, fmt.Errorf("invalid secondary click by selector params: %v", action.Params)
}
func (t *ToolSecondaryClickBySelector) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming secondary click operation",
"selector": "string: CSS selector or XPath of the element that was right-clicked",
}
}
// ToolWebCloseTab implements the web_close_tab tool call.
type ToolWebCloseTab struct{}
@@ -2370,6 +2591,13 @@ func (t *ToolWebCloseTab) ConvertActionToCallToolRequest(action MobileAction) (m
return buildMCPCallToolRequest(t.Name(), arguments), nil
}
func (t *ToolWebCloseTab) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming browser tab was closed",
"tabIndex": "int: Index of the tab that was closed",
}
}
// ToolSetIme implements the set_ime tool call.
type ToolSetIme struct{}
@@ -2419,6 +2647,13 @@ func (t *ToolSetIme) ConvertActionToCallToolRequest(action MobileAction) (mcp.Ca
return mcp.CallToolRequest{}, fmt.Errorf("invalid set ime params: %v", action.Params)
}
func (t *ToolSetIme) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming IME was set",
"ime": "string: Input method editor that was set",
}
}
// ToolGetSource implements the get_source tool call.
type ToolGetSource struct{}
@@ -2468,6 +2703,14 @@ func (t *ToolGetSource) ConvertActionToCallToolRequest(action MobileAction) (mcp
return mcp.CallToolRequest{}, fmt.Errorf("invalid get source params: %v", action.Params)
}
func (t *ToolGetSource) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming UI source was retrieved",
"packageName": "string: Package name of the app whose source was retrieved",
"source": "string: UI hierarchy/source tree data in XML or JSON format",
}
}
// ToolSleep implements the sleep tool call.
type ToolSleep struct{}
@@ -2526,6 +2769,13 @@ func (t *ToolSleep) ConvertActionToCallToolRequest(action MobileAction) (mcp.Cal
return buildMCPCallToolRequest(t.Name(), arguments), nil
}
func (t *ToolSleep) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming sleep operation completed",
"seconds": "float64: Duration in seconds that was slept",
}
}
// ToolSleepMS implements the sleep_ms tool call.
type ToolSleepMS struct{}
@@ -2577,6 +2827,13 @@ func (t *ToolSleepMS) ConvertActionToCallToolRequest(action MobileAction) (mcp.C
return buildMCPCallToolRequest(t.Name(), arguments), nil
}
func (t *ToolSleepMS) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming sleep operation completed",
"milliseconds": "int64: Duration in milliseconds that was slept",
}
}
// ToolSleepRandom implements the sleep_random tool call.
type ToolSleepRandom struct{}
@@ -2618,6 +2875,14 @@ func (t *ToolSleepRandom) ConvertActionToCallToolRequest(action MobileAction) (m
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep random params: %v", action.Params)
}
func (t *ToolSleepRandom) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming random sleep operation completed",
"params": "[]float64: Parameters used for random duration calculation",
"actualDuration": "float64: Actual duration that was slept (in seconds)",
}
}
// ToolClosePopups implements the close_popups tool call.
type ToolClosePopups struct{}
@@ -2656,6 +2921,13 @@ func (t *ToolClosePopups) ConvertActionToCallToolRequest(action MobileAction) (m
return buildMCPCallToolRequest(t.Name(), map[string]any{}), nil
}
func (t *ToolClosePopups) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming popups were closed",
"popupsClosed": "int: Number of popup windows or dialogs that were closed",
}
}
// ToolAIAction implements the ai_action tool call.
type ToolAIAction struct{}
@@ -2705,6 +2977,14 @@ func (t *ToolAIAction) ConvertActionToCallToolRequest(action MobileAction) (mcp.
return mcp.CallToolRequest{}, fmt.Errorf("invalid AI action params: %v", action.Params)
}
func (t *ToolAIAction) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming AI action was performed",
"prompt": "string: Natural language prompt that was processed",
"actionTaken": "string: Description of the specific action that was taken by AI",
}
}
// ToolFinished implements the finished tool call.
type ToolFinished struct{}
@@ -2743,6 +3023,14 @@ func (t *ToolFinished) ConvertActionToCallToolRequest(action MobileAction) (mcp.
return mcp.CallToolRequest{}, fmt.Errorf("invalid finished params: %v", action.Params)
}
func (t *ToolFinished) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming task completion",
"content": "string: Completion reason or result description",
"taskCompleted": "bool: Boolean indicating task was successfully finished",
}
}
func getFloat64ValueOrDefault(value float64, defaultValue float64) float64 {
if value == 0 {
return defaultValue

View File

@@ -69,6 +69,7 @@ type ActionTool interface {
Options() []mcp.ToolOption // MCP 选项定义
Implement() server.ToolHandlerFunc // 工具实现逻辑
ConvertActionToCallToolRequest(action MobileAction) (mcp.CallToolRequest, error) // 动作转换
ReturnSchema() map[string]string // 返回值结构描述
}
```
@@ -100,6 +101,12 @@ func (t *ToolTapXY) Implement() server.ToolHandlerFunc {
return mcp.NewToolResultText("操作成功"), nil
}
}
func (t *ToolTapXY) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming tap operation at specified coordinates",
}
}
```
### 2. 统一参数处理
@@ -153,6 +160,20 @@ if err != nil {
}
```
### 5. 返回值结构化描述
每个工具都提供详细的返回值类型信息:
```go
func (t *ToolScreenShot) ReturnSchema() map[string]string {
return map[string]string{
"image": "string: Base64 encoded screenshot image in JPEG format",
"name": "string: Image name identifier (typically 'screenshot')",
"type": "string: MIME type of the image (image/jpeg)",
}
}
```
## 🔧 如何扩展接入新工具
### 步骤 1: 定义工具结构体
@@ -256,7 +277,20 @@ func (t *ToolLongPress) ConvertActionToCallToolRequest(action MobileAction) (mcp
}
```
### 步骤 5: 注册工具
### 步骤 5: 定义返回值结构
```go
func (t *ToolLongPress) ReturnSchema() map[string]string {
return map[string]string{
"message": "string: Success message confirming long press operation",
"x": "float64: X coordinate where long press was performed",
"y": "float64: Y coordinate where long press was performed",
"duration": "float64: Duration of the long press in seconds",
}
}
```
### 步骤 6: 注册工具
`registerTools()` 方法中添加新工具:
@@ -271,7 +305,7 @@ func (s *MCPServer4XTDriver) registerTools() {
}
```
### 步骤 6: 添加单元测试
### 步骤 7: 添加单元测试
```go
func TestToolLongPress(t *testing.T) {
@@ -285,6 +319,11 @@ func TestToolLongPress(t *testing.T) {
options := tool.Options()
assert.NotEmpty(t, options)
// 测试返回值结构
returnSchema := tool.ReturnSchema()
assert.Contains(t, returnSchema["message"], "string:")
assert.Contains(t, returnSchema["x"], "float64:")
// 测试动作转换
action := MobileAction{
Method: option.ACTION_LongPress,
@@ -360,6 +399,17 @@ if unifiedReq.AntiRisk {
}
```
### 6. 返回值类型规范
```go
// 标准返回值类型前缀
"message": "string: 描述信息"
"x": "float64: X坐标值"
"count": "int: 数量"
"success": "bool: 成功状态"
"items": "[]string: 字符串数组"
"data": "object: 复杂对象"
```
## 🚀 高级特性
### 1. 反作弊支持
@@ -396,7 +446,11 @@ for _, point := range unifiedReq.Points {
#### list_available_devices
**功能**: 发现所有可用的设备和模拟器
**参数**: 无
**返回**: JSON 格式的设备列表
**返回值类型**:
- `androidDevices` ([]string): Android 设备序列号列表
- `iosDevices` ([]string): iOS 设备 UDID 列表
**返回示例**:
```json
{
"androidDevices": ["emulator-5554", "device-serial"],
@@ -410,6 +464,9 @@ for _, point := range unifiedReq.Points {
- `platform` (string): "android" | "ios" | "web" | "harmony"
- `serial` (string): 设备序列号或 UDID
**返回值类型**:
- `message` (string): 包含选中设备 UUID 的成功消息
---
### 👆 触摸操作工具
@@ -422,6 +479,9 @@ for _, point := range unifiedReq.Points {
- `duration` (number, 可选): 点击持续时间(秒)
- `anti_risk` (boolean, 可选): 启用反作弊
**返回值类型**:
- `message` (string): 确认在指定坐标点击操作的成功消息
#### tap_abs_xy
**功能**: 在绝对像素坐标点击
**参数**:
@@ -430,6 +490,9 @@ for _, point := range unifiedReq.Points {
- `duration` (number, 可选): 点击持续时间(秒)
- `anti_risk` (boolean, 可选): 启用反作弊
**返回值类型**:
- `message` (string): 确认在绝对坐标点击操作的成功消息
#### tap_ocr
**功能**: 通过 OCR 识别文本并点击
**参数**:
@@ -437,18 +500,27 @@ for _, point := range unifiedReq.Points {
- `ignore_NotFoundError` (boolean, 可选): 忽略未找到错误
- `regex` (boolean, 可选): 使用正则表达式匹配
**返回值类型**:
- `message` (string): 确认操作完成的成功消息
#### tap_cv
**功能**: 通过计算机视觉识别图像并点击
**参数**:
- `imagePath` (string): 模板图像路径
- `threshold` (number, 可选): 匹配阈值
**返回值类型**:
- `message` (string): 确认操作完成的成功消息
#### double_tap_xy
**功能**: 在指定坐标双击
**参数**:
- `x` (number): X 坐标
- `y` (number): Y 坐标
**返回值类型**:
- `message` (string): 确认操作完成的成功消息
---
### 🔄 手势操作工具
@@ -457,6 +529,14 @@ for _, point := range unifiedReq.Points {
**功能**: 通用滑动 (自动检测方向或坐标)
**参数**: 支持方向滑动或坐标滑动两种模式
**返回值类型**:
- `message` (string): 确认滑动操作的成功消息
- `direction` (string): 滑动方向 (方向滑动模式)
- `fromX` (float64): 起始 X 坐标 (坐标滑动模式)
- `fromY` (float64): 起始 Y 坐标 (坐标滑动模式)
- `toX` (float64): 结束 X 坐标 (坐标滑动模式)
- `toY` (float64): 结束 Y 坐标 (坐标滑动模式)
##### 方向滑动模式:
- `direction` (string): "up" | "down" | "left" | "right"
- `duration` (number, 可选): 滑动持续时间
@@ -468,6 +548,34 @@ for _, point := range unifiedReq.Points {
- `to_x` (number): 结束 X 坐标
- `to_y` (number): 结束 Y 坐标
#### swipe_direction
**功能**: 方向滑动
**参数**:
- `direction` (string): "up" | "down" | "left" | "right"
- `duration` (number, 可选): 滑动持续时间
- `press_duration` (number, 可选): 按压持续时间
**返回值类型**:
- `message` (string): 确认方向滑动的成功消息
- `direction` (string): 滑动的方向 (up/down/left/right)
#### swipe_coordinate
**功能**: 坐标滑动
**参数**:
- `from_x` (number): 起始 X 坐标
- `from_y` (number): 起始 Y 坐标
- `to_x` (number): 结束 X 坐标
- `to_y` (number): 结束 Y 坐标
- `duration` (number, 可选): 滑动持续时间
- `press_duration` (number, 可选): 按压持续时间
**返回值类型**:
- `message` (string): 确认坐标滑动的成功消息
- `fromX` (float64): 滑动起始 X 坐标
- `fromY` (float64): 滑动起始 Y 坐标
- `toX` (float64): 滑动结束 X 坐标
- `toY` (float64): 滑动结束 Y 坐标
#### drag
**功能**: 拖拽操作
**参数**:
@@ -477,6 +585,13 @@ for _, point := range unifiedReq.Points {
- `to_y` (number): 结束 Y 坐标
- `duration` (number, 可选): 拖拽持续时间(毫秒)
**返回值类型**:
- `message` (string): 确认拖拽操作的成功消息
- `fromX` (float64): 拖拽起始 X 坐标
- `fromY` (float64): 拖拽起始 Y 坐标
- `toX` (float64): 拖拽结束 X 坐标
- `toY` (float64): 拖拽结束 Y 坐标
#### swipe_to_tap_app
**功能**: 滑动查找并点击应用
**参数**:
@@ -484,6 +599,10 @@ for _, point := range unifiedReq.Points {
- `max_retry_times` (number, 可选): 最大重试次数
- `ignore_NotFoundError` (boolean, 可选): 忽略未找到错误
**返回值类型**:
- `message` (string): 确认找到并点击应用的成功消息
- `appName` (string): 找到并点击的应用名称
#### swipe_to_tap_text
**功能**: 滑动查找并点击文本
**参数**:
@@ -491,12 +610,21 @@ for _, point := range unifiedReq.Points {
- `max_retry_times` (number, 可选): 最大重试次数
- `regex` (boolean, 可选): 使用正则表达式
**返回值类型**:
- `message` (string): 确认找到并点击文本的成功消息
- `text` (string): 找到并点击的文本内容
#### swipe_to_tap_texts
**功能**: 滑动查找并点击多个文本中的一个
**参数**:
- `texts` (array): 文本数组
- `max_retry_times` (number, 可选): 最大重试次数
**返回值类型**:
- `message` (string): 确认找到并点击其中一个文本的成功消息
- `texts` ([]string): 搜索的文本选项列表
- `foundText` (string): 实际找到并点击的特定文本
---
### ⌨️ 输入操作工具
@@ -506,6 +634,10 @@ for _, point := range unifiedReq.Points {
**参数**:
- `text` (string): 要输入的文本
**返回值类型**:
- `message` (string): 确认文本输入的成功消息
- `text` (string): 输入到字段中的文本内容
#### press_button
**功能**: 按设备按键
**参数**:
@@ -513,14 +645,24 @@ for _, point := range unifiedReq.Points {
- Android: "BACK", "HOME", "VOLUME_UP", "VOLUME_DOWN", "ENTER"
- iOS: "HOME", "VOLUME_UP", "VOLUME_DOWN"
**返回值类型**:
- `message` (string): 确认按键操作的成功消息
- `button` (string): 被按下的按键名称
#### home
**功能**: 按 Home 键
**参数**: 无
**返回值类型**:
- `message` (string): 确认 Home 键被按下的成功消息
#### back
**功能**: 按返回键 (仅 Android)
**参数**: 无
**返回值类型**:
- `message` (string): 确认返回键被按下的成功消息
---
### 📱 应用管理工具
@@ -529,31 +671,52 @@ for _, point := range unifiedReq.Points {
**功能**: 列出设备上所有应用包名
**参数**: 无
**返回值类型**:
- `packages` ([]string): 设备上已安装应用包名列表
#### app_launch
**功能**: 启动应用
**参数**:
- `packageName` (string): 应用包名
**返回值类型**:
- `message` (string): 确认操作完成的成功消息
#### app_terminate
**功能**: 终止应用
**参数**:
- `packageName` (string): 应用包名
**返回值类型**:
- `message` (string): 确认操作完成的成功消息
#### app_install
**功能**: 安装应用
**参数**:
- `appUrl` (string): APK/IPA 文件路径或 URL
**返回值类型**:
- `message` (string): 确认应用安装的成功消息
- `appUrl` (string): 安装的应用 URL 或路径
#### app_uninstall
**功能**: 卸载应用
**参数**:
- `packageName` (string): 应用包名
**返回值类型**:
- `message` (string): 确认应用卸载的成功消息
- `packageName` (string): 被卸载的应用包名
#### app_clear
**功能**: 清除应用数据
**参数**:
- `packageName` (string): 应用包名
**返回值类型**:
- `message` (string): 确认应用数据和缓存被清除的成功消息
- `packageName` (string): 被清除的应用包名
---
### 📸 屏幕操作工具
@@ -561,18 +724,31 @@ for _, point := range unifiedReq.Points {
#### screenshot
**功能**: 截取屏幕截图
**参数**: 无
**返回**: Base64 编码的图像数据
**返回值类型**:
- `image` (string): JPEG 格式的 Base64 编码截图图像
- `name` (string): 图像名称标识符 (通常为 'screenshot')
- `type` (string): 图像的 MIME 类型 (image/jpeg)
#### get_screen_size
**功能**: 获取屏幕尺寸
**参数**: 无
**返回**: 屏幕宽度和高度 (像素)
**返回值类型**:
- `width` (int): 屏幕宽度 (像素)
- `height` (int): 屏幕高度 (像素)
- `message` (string): 包含屏幕尺寸的格式化消息
#### get_source
**功能**: 获取 UI 层次结构
**参数**:
- `packageName` (string, 可选): 指定应用包名
**返回值类型**:
- `message` (string): 确认 UI 源码获取的成功消息
- `packageName` (string): 获取源码的应用包名
- `source` (string): XML 或 JSON 格式的 UI 层次/源码树数据
---
### ⏱️ 时间控制工具
@@ -582,16 +758,29 @@ for _, point := range unifiedReq.Points {
**参数**:
- `seconds` (number): 等待秒数
**返回值类型**:
- `message` (string): 确认睡眠操作完成的成功消息
- `seconds` (float64): 睡眠的持续时间 (秒)
#### sleep_ms
**功能**: 等待指定毫秒数
**参数**:
- `milliseconds` (number): 等待毫秒数
**返回值类型**:
- `message` (string): 确认睡眠操作完成的成功消息
- `milliseconds` (int64): 睡眠的持续时间 (毫秒)
#### sleep_random
**功能**: 随机等待
**参数**:
- `params` (array): 随机参数数组
**返回值类型**:
- `message` (string): 确认随机睡眠操作完成的成功消息
- `params` ([]float64): 用于随机持续时间计算的参数
- `actualDuration` (float64): 实际睡眠的持续时间 (秒)
---
### 🛠️ 实用工具
@@ -601,10 +790,18 @@ for _, point := range unifiedReq.Points {
**参数**:
- `ime` (string): 输入法包名
**返回值类型**:
- `message` (string): 确认 IME 设置的成功消息
- `ime` (string): 设置的输入法编辑器
#### close_popups
**功能**: 关闭弹窗
**参数**: 无
**返回值类型**:
- `message` (string): 确认弹窗关闭的成功消息
- `popupsClosed` (int): 关闭的弹窗或对话框数量
---
### 🌐 Web 操作工具
@@ -617,32 +814,57 @@ for _, point := range unifiedReq.Points {
- `captcha` (string, 可选): 验证码
- `password` (string, 可选): 密码
**返回值类型**:
- `message` (string): 确认 Web 登录完成的成功消息
- `loginResult` (object): 登录操作的结果 (成功/失败详情)
#### secondary_click
**功能**: 右键点击
**参数**:
- `x` (number): X 坐标
- `y` (number): Y 坐标
**返回值类型**:
- `message` (string): 确认辅助点击 (右键) 操作的成功消息
- `x` (float64): 执行辅助点击的 X 坐标
- `y` (float64): 执行辅助点击的 Y 坐标
#### hover_by_selector
**功能**: 悬停在选择器元素上
**参数**:
- `selector` (string): CSS 选择器或 XPath
**返回值类型**:
- `message` (string): 确认悬停操作的成功消息
- `selector` (string): 悬停元素的 CSS 选择器或 XPath
#### tap_by_selector
**功能**: 点击选择器元素
**参数**:
- `selector` (string): CSS 选择器或 XPath
**返回值类型**:
- `message` (string): 确认点击操作的成功消息
- `selector` (string): 被点击元素的 CSS 选择器或 XPath
#### secondary_click_by_selector
**功能**: 右键点击选择器元素
**参数**:
- `selector` (string): CSS 选择器或 XPath
**返回值类型**:
- `message` (string): 确认辅助点击操作的成功消息
- `selector` (string): 被右键点击元素的 CSS 选择器或 XPath
#### web_close_tab
**功能**: 关闭浏览器标签页
**参数**:
- `tabIndex` (number): 标签页索引
**返回值类型**:
- `message` (string): 确认浏览器标签页关闭的成功消息
- `tabIndex` (int): 被关闭的标签页索引
---
### 🤖 AI 操作工具
@@ -652,11 +874,21 @@ for _, point := range unifiedReq.Points {
**参数**:
- `prompt` (string): 自然语言指令
**返回值类型**:
- `message` (string): 确认 AI 操作执行的成功消息
- `prompt` (string): 处理的自然语言提示
- `actionTaken` (string): AI 执行的具体操作描述
#### finished
**功能**: 标记任务完成
**参数**:
- `content` (string): 完成信息
**返回值类型**:
- `message` (string): 确认任务完成的成功消息
- `content` (string): 完成原因或结果描述
- `taskCompleted` (bool): 指示任务成功完成的布尔值
---
### 📋 通用参数说明
@@ -754,3 +986,23 @@ for _, point := range unifiedReq.Points {
4. **平台差异**: 不同平台支持的功能可能有差异
5. **错误处理**: 建议启用适当的错误忽略选项
6. **性能考虑**: 避免过于频繁的操作,适当添加等待时间
7. **返回值类型**: 所有返回值都包含明确的类型信息,便于 AI 模型理解和处理
### 📊 返回值类型系统
HttpRunner MCP Server 为所有工具提供了完整的返回值类型描述,采用 `类型: 描述` 的格式:
#### 支持的数据类型
- **string**: 文本消息、名称、描述等
- **int**: 整数值如屏幕宽度、高度、标签索引等
- **int64**: 长整型如毫秒数
- **float64**: 浮点数如坐标值、时间等
- **bool**: 布尔值如任务完成状态
- **[]string**: 字符串数组如设备列表、文本选项等
- **object**: 复杂对象如登录结果
#### 类型信息的作用
1. **AI 模型理解**: 帮助 AI 模型正确解析和使用返回值
2. **开发调试**: 为开发者提供清晰的接口文档
3. **类型安全**: 确保数据类型的一致性和可预测性
4. **自动化测试**: 支持基于类型的自动化验证

View File

@@ -82,6 +82,11 @@ func (c *MCPClient4XTDriver) Close() error {
return nil
}
// GetToolByAction implements ActionToolProvider interface
func (c *MCPClient4XTDriver) GetToolByAction(actionName option.ActionName) ActionTool {
return c.Server.GetToolByAction(actionName)
}
func (dExt *XTDriver) ExecuteAction(ctx context.Context, action MobileAction) (err error) {
// Find the corresponding tool for this action method
tool := dExt.client.Server.GetToolByAction(action.Method)