mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 23:51:25 +08:00
refactor: move server models to uixt/types
This commit is contained in:
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2505201326
|
v5.0.0-beta-2505201421
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package server
|
package mcphost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/httprunner/httprunner/v5/internal/version"
|
"github.com/httprunner/httprunner/v5/internal/version"
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
"github.com/httprunner/httprunner/v5/uixt"
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||||
|
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||||
"github.com/mark3labs/mcp-go/mcp"
|
"github.com/mark3labs/mcp-go/mcp"
|
||||||
"github.com/mark3labs/mcp-go/server"
|
"github.com/mark3labs/mcp-go/server"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@@ -20,7 +21,8 @@ import (
|
|||||||
// MCPServer4XTDriver wraps a MCPServer to expose XTDriver functionality via MCP protocol.
|
// MCPServer4XTDriver wraps a MCPServer to expose XTDriver functionality via MCP protocol.
|
||||||
type MCPServer4XTDriver struct {
|
type MCPServer4XTDriver struct {
|
||||||
mcpServer *server.MCPServer
|
mcpServer *server.MCPServer
|
||||||
driverCache sync.Map // key is serial, value is *XTDriver
|
driverCache sync.Map // key is serial, value is *XTDriver
|
||||||
|
tools []mcp.Tool // 本地维护的工具列表
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMCPServer creates a new MCP server for XTDriver and registers all tools.
|
// NewMCPServer creates a new MCP server for XTDriver and registers all tools.
|
||||||
@@ -50,9 +52,10 @@ func (ums *MCPServer4XTDriver) addTools() {
|
|||||||
[]mcp.ToolOption{mcp.WithDescription("Taps on the device screen at the given coordinates.")},
|
[]mcp.ToolOption{mcp.WithDescription("Taps on the device screen at the given coordinates.")},
|
||||||
commonToolOptions...,
|
commonToolOptions...,
|
||||||
)
|
)
|
||||||
tapParams = append(tapParams, generateMCPOptions(TapRequest{})...)
|
tapParams = append(tapParams, generateMCPOptions(types.TapRequest{})...)
|
||||||
tapXYTool := mcp.NewTool("tap_xy", tapParams...)
|
tapXYTool := mcp.NewTool("tap_xy", tapParams...)
|
||||||
ums.mcpServer.AddTool(tapXYTool, ums.handleTapXY)
|
ums.mcpServer.AddTool(tapXYTool, ums.handleTapXY)
|
||||||
|
ums.tools = append(ums.tools, tapXYTool)
|
||||||
log.Info().Str("name", tapXYTool.Name).Msg("Register tool")
|
log.Info().Str("name", tapXYTool.Name).Msg("Register tool")
|
||||||
|
|
||||||
// Swipe Tool
|
// Swipe Tool
|
||||||
@@ -60,9 +63,10 @@ func (ums *MCPServer4XTDriver) addTools() {
|
|||||||
[]mcp.ToolOption{mcp.WithDescription("Swipes on the device screen from one point to another.")},
|
[]mcp.ToolOption{mcp.WithDescription("Swipes on the device screen from one point to another.")},
|
||||||
commonToolOptions...,
|
commonToolOptions...,
|
||||||
)
|
)
|
||||||
swipeParams = append(swipeParams, generateMCPOptions(DragRequest{})...)
|
swipeParams = append(swipeParams, generateMCPOptions(types.DragRequest{})...)
|
||||||
swipeTool := mcp.NewTool("swipe", swipeParams...)
|
swipeTool := mcp.NewTool("swipe", swipeParams...)
|
||||||
ums.mcpServer.AddTool(swipeTool, ums.handleSwipe)
|
ums.mcpServer.AddTool(swipeTool, ums.handleSwipe)
|
||||||
|
ums.tools = append(ums.tools, swipeTool)
|
||||||
log.Info().Str("name", swipeTool.Name).Msg("Register tool")
|
log.Info().Str("name", swipeTool.Name).Msg("Register tool")
|
||||||
|
|
||||||
// ScreenShot Tool
|
// ScreenShot Tool
|
||||||
@@ -70,6 +74,7 @@ func (ums *MCPServer4XTDriver) addTools() {
|
|||||||
mcp.WithDescription("Takes a screenshot of the device screen and returns it as a base64 encoded string."),
|
mcp.WithDescription("Takes a screenshot of the device screen and returns it as a base64 encoded string."),
|
||||||
)
|
)
|
||||||
ums.mcpServer.AddTool(screenShotTool, ums.handleScreenShot)
|
ums.mcpServer.AddTool(screenShotTool, ums.handleScreenShot)
|
||||||
|
ums.tools = append(ums.tools, screenShotTool)
|
||||||
log.Info().Str("name", screenShotTool.Name).Msg("Register tool")
|
log.Info().Str("name", screenShotTool.Name).Msg("Register tool")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +84,7 @@ func (ums *MCPServer4XTDriver) handleTapXY(ctx context.Context, request mcp.Call
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var tapReq TapRequest
|
var tapReq types.TapRequest
|
||||||
if err := mapToStruct(request.Params.Arguments, &tapReq); err != nil {
|
if err := mapToStruct(request.Params.Arguments, &tapReq); err != nil {
|
||||||
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
||||||
}
|
}
|
||||||
@@ -103,7 +108,7 @@ func (ums *MCPServer4XTDriver) handleSwipe(ctx context.Context, request mcp.Call
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var swipeReq DragRequest
|
var swipeReq types.DragRequest
|
||||||
if err := mapToStruct(request.Params.Arguments, &swipeReq); err != nil {
|
if err := mapToStruct(request.Params.Arguments, &swipeReq); err != nil {
|
||||||
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
return mcp.NewToolResultError("parse parameters error: " + err.Error()), nil
|
||||||
}
|
}
|
||||||
@@ -253,3 +258,18 @@ var commonToolOptions = []mcp.ToolOption{
|
|||||||
mcp.WithString("platform", mcp.Required(), mcp.Description("Device platform: android/ios/browser")),
|
mcp.WithString("platform", mcp.Required(), mcp.Description("Device platform: android/ios/browser")),
|
||||||
mcp.WithString("serial", mcp.Required(), mcp.Description("Device serial/udid/browser id")),
|
mcp.WithString("serial", mcp.Required(), mcp.Description("Device serial/udid/browser id")),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListTools 返回所有注册的 mcp.Tool
|
||||||
|
func (s *MCPServer4XTDriver) ListTools() []mcp.Tool {
|
||||||
|
return s.tools
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTool 根据名称返回 mcp.Tool 指针
|
||||||
|
func (s *MCPServer4XTDriver) GetTool(name string) *mcp.Tool {
|
||||||
|
for i := range s.tools {
|
||||||
|
if s.tools[i].Name == name {
|
||||||
|
return &s.tools[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -4,12 +4,6 @@ import (
|
|||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TapRequest struct {
|
|
||||||
X float64 `json:"x" binding:"required" desc:"X coordinate (0.0~1.0 for percent, or absolute pixel value)"`
|
|
||||||
Y float64 `json:"y" binding:"required" desc:"Y coordinate (0.0~1.0 for percent, or absolute pixel value)"`
|
|
||||||
Duration float64 `json:"duration" desc:"Tap duration in seconds (optional)"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type uploadRequest struct {
|
type uploadRequest struct {
|
||||||
X float64 `json:"x"`
|
X float64 `json:"x"`
|
||||||
Y float64 `json:"y"`
|
Y float64 `json:"y"`
|
||||||
@@ -17,15 +11,6 @@ type uploadRequest struct {
|
|||||||
FileFormat string `json:"file_format"`
|
FileFormat string `json:"file_format"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DragRequest struct {
|
|
||||||
FromX float64 `json:"from_x" binding:"required" desc:"Starting X-coordinate (percentage, 0.0 to 1.0)"`
|
|
||||||
FromY float64 `json:"from_y" binding:"required" desc:"Starting Y-coordinate (percentage, 0.0 to 1.0)"`
|
|
||||||
ToX float64 `json:"to_x" binding:"required" desc:"Ending X-coordinate (percentage, 0.0 to 1.0)"`
|
|
||||||
ToY float64 `json:"to_y" binding:"required" desc:"Ending Y-coordinate (percentage, 0.0 to 1.0)"`
|
|
||||||
Duration float64 `json:"duration" desc:"Swipe duration in milliseconds (optional)"`
|
|
||||||
PressDuration float64 `json:"press_duration" desc:"Press duration in milliseconds (optional)"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputRequest struct {
|
type InputRequest struct {
|
||||||
Text string `json:"text" binding:"required"`
|
Text string `json:"text" binding:"required"`
|
||||||
Frequency int `json:"frequency"` // only iOS
|
Frequency int `json:"frequency"` // only iOS
|
||||||
|
|||||||
+5
-4
@@ -4,10 +4,11 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
"github.com/httprunner/httprunner/v5/uixt"
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||||
|
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *Router) tapHandler(c *gin.Context) {
|
func (r *Router) tapHandler(c *gin.Context) {
|
||||||
var tapReq TapRequest
|
var tapReq types.TapRequest
|
||||||
if err := c.ShouldBindJSON(&tapReq); err != nil {
|
if err := c.ShouldBindJSON(&tapReq); err != nil {
|
||||||
RenderErrorValidateRequest(c, err)
|
RenderErrorValidateRequest(c, err)
|
||||||
return
|
return
|
||||||
@@ -30,7 +31,7 @@ func (r *Router) tapHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) rightClickHandler(c *gin.Context) {
|
func (r *Router) rightClickHandler(c *gin.Context) {
|
||||||
var rightClickReq TapRequest
|
var rightClickReq types.TapRequest
|
||||||
if err := c.ShouldBindJSON(&rightClickReq); err != nil {
|
if err := c.ShouldBindJSON(&rightClickReq); err != nil {
|
||||||
RenderErrorValidateRequest(c, err)
|
RenderErrorValidateRequest(c, err)
|
||||||
return
|
return
|
||||||
@@ -117,7 +118,7 @@ func (r *Router) scrollHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) doubleTapHandler(c *gin.Context) {
|
func (r *Router) doubleTapHandler(c *gin.Context) {
|
||||||
var tapReq TapRequest
|
var tapReq types.TapRequest
|
||||||
if err := c.ShouldBindJSON(&tapReq); err != nil {
|
if err := c.ShouldBindJSON(&tapReq); err != nil {
|
||||||
RenderErrorValidateRequest(c, err)
|
RenderErrorValidateRequest(c, err)
|
||||||
return
|
return
|
||||||
@@ -137,7 +138,7 @@ func (r *Router) doubleTapHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) dragHandler(c *gin.Context) {
|
func (r *Router) dragHandler(c *gin.Context) {
|
||||||
var dragReq DragRequest
|
var dragReq types.DragRequest
|
||||||
if err := c.ShouldBindJSON(&dragReq); err != nil {
|
if err := c.ShouldBindJSON(&dragReq); err != nil {
|
||||||
RenderErrorValidateRequest(c, err)
|
RenderErrorValidateRequest(c, err)
|
||||||
return
|
return
|
||||||
|
|||||||
+4
-3
@@ -8,6 +8,7 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,14 +18,14 @@ func TestTapHandler(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
path string
|
path string
|
||||||
tapReq TapRequest
|
tapReq types.TapRequest
|
||||||
wantStatus int
|
wantStatus int
|
||||||
wantResp HttpResponse
|
wantResp HttpResponse
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "tap abs xy",
|
name: "tap abs xy",
|
||||||
path: fmt.Sprintf("/api/v1/android/%s/ui/tap", "4622ca24"),
|
path: fmt.Sprintf("/api/v1/android/%s/ui/tap", "4622ca24"),
|
||||||
tapReq: TapRequest{
|
tapReq: types.TapRequest{
|
||||||
X: 500,
|
X: 500,
|
||||||
Y: 800,
|
Y: 800,
|
||||||
Duration: 0,
|
Duration: 0,
|
||||||
@@ -39,7 +40,7 @@ func TestTapHandler(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "tap relative xy",
|
name: "tap relative xy",
|
||||||
path: fmt.Sprintf("/api/v1/android/%s/ui/tap", "4622ca24"),
|
path: fmt.Sprintf("/api/v1/android/%s/ui/tap", "4622ca24"),
|
||||||
tapReq: TapRequest{
|
tapReq: types.TapRequest{
|
||||||
X: 0.5,
|
X: 0.5,
|
||||||
Y: 0.6,
|
Y: 0.6,
|
||||||
Duration: 0,
|
Duration: 0,
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type TapRequest struct {
|
||||||
|
X float64 `json:"x" binding:"required" desc:"X coordinate (0.0~1.0 for percent, or absolute pixel value)"`
|
||||||
|
Y float64 `json:"y" binding:"required" desc:"Y coordinate (0.0~1.0 for percent, or absolute pixel value)"`
|
||||||
|
Duration float64 `json:"duration" desc:"Tap duration in seconds (optional)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DragRequest struct {
|
||||||
|
FromX float64 `json:"from_x" binding:"required" desc:"Starting X-coordinate (percentage, 0.0 to 1.0)"`
|
||||||
|
FromY float64 `json:"from_y" binding:"required" desc:"Starting Y-coordinate (percentage, 0.0 to 1.0)"`
|
||||||
|
ToX float64 `json:"to_x" binding:"required" desc:"Ending X-coordinate (percentage, 0.0 to 1.0)"`
|
||||||
|
ToY float64 `json:"to_y" binding:"required" desc:"Ending Y-coordinate (percentage, 0.0 to 1.0)"`
|
||||||
|
Duration float64 `json:"duration" desc:"Swipe duration in milliseconds (optional)"`
|
||||||
|
PressDuration float64 `json:"press_duration" desc:"Press duration in milliseconds (optional)"`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user