feat: load uixt mcp server in mcphost

This commit is contained in:
lilong.129
2025-05-20 16:42:09 +08:00
parent 5066c64368
commit 83434cca1e
4 changed files with 85 additions and 10 deletions

16
cmd/mcpserver.go Normal file
View File

@@ -0,0 +1,16 @@
package cmd
import (
"github.com/httprunner/httprunner/v5/mcphost"
"github.com/spf13/cobra"
)
var CmdMCPServer = &cobra.Command{
Use: "mcp-server",
Short: "Start MCP server for UI automation",
Long: `Start MCP server for UI automation, expose device driver via MCP protocol`,
RunE: func(cmd *cobra.Command, args []string) error {
mcpServer := mcphost.NewMCPServer()
return mcpServer.Start()
},
}

View File

@@ -1 +1 @@
v5.0.0-beta-2505201421
v5.0.0-beta-2505201707

View File

@@ -61,6 +61,14 @@ func NewMCPHost(configPath string) (*MCPHost, error) {
// InitServers initializes all MCP servers
func (h *MCPHost) InitServers(ctx context.Context) error {
// initialize uixt MCP server
h.connections["uixt"] = &Connection{
Client: &MCPClient4XTDriver{
server: NewMCPServer(),
},
Config: nil,
}
for name, server := range h.config.MCPServers {
if server.Config.IsDisabled() {
continue
@@ -299,27 +307,33 @@ func (h *MCPHost) GetEinoToolInfos(ctx context.Context) ([]*schema.ToolInfo, err
var tools []*schema.ToolInfo
for _, serverTools := range results {
if serverTools.Err != nil {
log.Error().Err(serverTools.Err).Str("server", serverTools.ServerName).Msg("failed to get tools")
log.Error().Err(serverTools.Err).
Str("server", serverTools.ServerName).Msg("failed to get tools")
continue
}
var toolNames []string
for _, tool := range serverTools.Tools {
einoTool, err := h.GetEinoTool(ctx, serverTools.ServerName, tool.Name)
if err != nil {
log.Error().Err(err).Str("server", serverTools.ServerName).Str("tool", tool.Name).Msg("failed to get eino tool")
log.Error().Err(err).Str("server", serverTools.ServerName).
Str("tool", tool.Name).Msg("failed to get eino tool")
continue
}
einoToolInfo, err := einoTool.Info(ctx)
if err != nil {
log.Error().Err(err).Str("server", serverTools.ServerName).Str("tool", tool.Name).Msg("failed to get eino tool info")
log.Error().Err(err).Str("server", serverTools.ServerName).
Str("tool", tool.Name).Msg("failed to get eino tool info")
continue
}
einoToolInfo.Name = fmt.Sprintf("%s__%s", serverTools.ServerName, tool.Name)
tools = append(tools, einoToolInfo)
toolNames = append(toolNames, tool.Name)
}
log.Debug().Str("server", serverTools.ServerName).
Strs("tools", toolNames).Msg("loaded MCP tools")
}
log.Info().Int("count", len(tools)).Msg("eino tool infos loaded")
return tools, nil
}

View File

@@ -13,16 +13,49 @@ import (
"github.com/httprunner/httprunner/v5/uixt"
"github.com/httprunner/httprunner/v5/uixt/option"
"github.com/httprunner/httprunner/v5/uixt/types"
"github.com/mark3labs/mcp-go/client"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/rs/zerolog/log"
)
// MCPClient4XTDriver is a minimal MCP client that only implements the methods used by the host
type MCPClient4XTDriver struct {
client.MCPClient
server *MCPServer4XTDriver
}
func (c *MCPClient4XTDriver) ListTools(ctx context.Context, req mcp.ListToolsRequest) (*mcp.ListToolsResult, error) {
tools := c.server.ListTools()
return &mcp.ListToolsResult{Tools: tools}, nil
}
func (c *MCPClient4XTDriver) CallTool(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
handler := c.server.GetHandler(req.Params.Name)
if handler == nil {
return mcp.NewToolResultError(fmt.Sprintf("handler for tool %s not found", req.Params.Name)), nil
}
return handler(ctx, req)
}
func (c *MCPClient4XTDriver) Initialize(ctx context.Context, req mcp.InitializeRequest) (*mcp.InitializeResult, error) {
// no need to initialize for local server
return &mcp.InitializeResult{}, nil
}
func (c *MCPClient4XTDriver) Close() error {
// no need to close for local server
return nil
}
type toolCall = func(context.Context, mcp.CallToolRequest) (*mcp.CallToolResult, error)
// MCPServer4XTDriver wraps a MCPServer to expose XTDriver functionality via MCP protocol.
type MCPServer4XTDriver struct {
mcpServer *server.MCPServer
driverCache sync.Map // key is serial, value is *XTDriver
tools []mcp.Tool // 本地维护的工具列表
driverCache sync.Map // key is serial, value is *XTDriver
tools []mcp.Tool // tools list for uixt
handlerMap map[string]toolCall // tool name to handler
}
// NewMCPServer creates a new MCP server for XTDriver and registers all tools.
@@ -33,7 +66,8 @@ func NewMCPServer() *MCPServer4XTDriver {
server.WithToolCapabilities(false),
)
s := &MCPServer4XTDriver{
mcpServer: mcpServer,
mcpServer: mcpServer,
handlerMap: make(map[string]toolCall),
}
s.addTools()
return s
@@ -56,6 +90,7 @@ func (ums *MCPServer4XTDriver) addTools() {
tapXYTool := mcp.NewTool("tap_xy", tapParams...)
ums.mcpServer.AddTool(tapXYTool, ums.handleTapXY)
ums.tools = append(ums.tools, tapXYTool)
ums.handlerMap[tapXYTool.Name] = ums.handleTapXY
log.Info().Str("name", tapXYTool.Name).Msg("Register tool")
// Swipe Tool
@@ -67,6 +102,7 @@ func (ums *MCPServer4XTDriver) addTools() {
swipeTool := mcp.NewTool("swipe", swipeParams...)
ums.mcpServer.AddTool(swipeTool, ums.handleSwipe)
ums.tools = append(ums.tools, swipeTool)
ums.handlerMap[swipeTool.Name] = ums.handleSwipe
log.Info().Str("name", swipeTool.Name).Msg("Register tool")
// ScreenShot Tool
@@ -75,6 +111,7 @@ func (ums *MCPServer4XTDriver) addTools() {
)
ums.mcpServer.AddTool(screenShotTool, ums.handleScreenShot)
ums.tools = append(ums.tools, screenShotTool)
ums.handlerMap[screenShotTool.Name] = ums.handleScreenShot
log.Info().Str("name", screenShotTool.Name).Msg("Register tool")
}
@@ -259,12 +296,12 @@ var commonToolOptions = []mcp.ToolOption{
mcp.WithString("serial", mcp.Required(), mcp.Description("Device serial/udid/browser id")),
}
// ListTools 返回所有注册的 mcp.Tool
// ListTools returns all registered tools
func (s *MCPServer4XTDriver) ListTools() []mcp.Tool {
return s.tools
}
// GetTool 根据名称返回 mcp.Tool 指针
// GetTool returns a pointer to the mcp.Tool with the given name
func (s *MCPServer4XTDriver) GetTool(name string) *mcp.Tool {
for i := range s.tools {
if s.tools[i].Name == name {
@@ -273,3 +310,11 @@ func (s *MCPServer4XTDriver) GetTool(name string) *mcp.Tool {
}
return nil
}
// GetHandler returns the tool handler for the given name
func (s *MCPServer4XTDriver) GetHandler(name string) toolCall {
if s.handlerMap == nil {
return nil
}
return s.handlerMap[name]
}