ada tools update
This commit is contained in:
736
mcp/main.go
736
mcp/main.go
@@ -380,7 +380,7 @@ func main() {
|
||||
// Register web_screenshot tool
|
||||
mcpServer.AddTool(mcp.Tool{
|
||||
Name: "web_screenshot_cremotemcp",
|
||||
Description: "Take a screenshot of the current page",
|
||||
Description: "Take a screenshot of the current page with optional zoom level and viewport size for accessibility testing",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
@@ -393,6 +393,18 @@ func main() {
|
||||
"description": "Capture full page",
|
||||
"default": false,
|
||||
},
|
||||
"zoom_level": map[string]any{
|
||||
"type": "number",
|
||||
"description": "Zoom level (e.g., 1.0, 2.0, 4.0) for accessibility testing",
|
||||
},
|
||||
"width": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Viewport width in pixels for responsive testing",
|
||||
},
|
||||
"height": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Viewport height in pixels for responsive testing",
|
||||
},
|
||||
"tab": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Tab ID (optional)",
|
||||
@@ -417,6 +429,19 @@ func main() {
|
||||
tab := getStringParam(params, "tab", cremoteServer.currentTab)
|
||||
timeout := getIntParam(params, "timeout", 5)
|
||||
|
||||
// Get optional zoom and viewport parameters
|
||||
var zoomLevel float64
|
||||
if zoomParam, ok := params["zoom_level"].(float64); ok {
|
||||
zoomLevel = zoomParam
|
||||
}
|
||||
var width, height int
|
||||
if widthParam, ok := params["width"].(float64); ok {
|
||||
width = int(widthParam)
|
||||
}
|
||||
if heightParam, ok := params["height"].(float64); ok {
|
||||
height = int(heightParam)
|
||||
}
|
||||
|
||||
if output == "" {
|
||||
return nil, fmt.Errorf("output parameter is required")
|
||||
}
|
||||
@@ -424,11 +449,38 @@ func main() {
|
||||
return nil, fmt.Errorf("no tab available - navigate to a page first")
|
||||
}
|
||||
|
||||
err := cremoteServer.client.TakeScreenshot(tab, output, fullPage, timeout)
|
||||
// Build command parameters
|
||||
cmdParams := map[string]string{
|
||||
"output": output,
|
||||
"full-page": strconv.FormatBool(fullPage),
|
||||
"tab": tab,
|
||||
"timeout": strconv.Itoa(timeout),
|
||||
}
|
||||
if zoomLevel > 0 {
|
||||
cmdParams["zoom_level"] = strconv.FormatFloat(zoomLevel, 'f', 1, 64)
|
||||
}
|
||||
if width > 0 {
|
||||
cmdParams["width"] = strconv.Itoa(width)
|
||||
}
|
||||
if height > 0 {
|
||||
cmdParams["height"] = strconv.Itoa(height)
|
||||
}
|
||||
|
||||
// Send command to daemon
|
||||
resp, err := cremoteServer.client.SendCommand("screenshot", cmdParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to take screenshot: %w", err)
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Screenshot failed: %s", resp.Error)),
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
cremoteServer.screenshots = append(cremoteServer.screenshots, output)
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
@@ -787,7 +839,7 @@ func main() {
|
||||
// Register console_command tool
|
||||
mcpServer.AddTool(mcp.Tool{
|
||||
Name: "console_command_cremotemcp",
|
||||
Description: "Execute a command in the browser console",
|
||||
Description: "Execute a command in the browser console with optional library injection (axe-core, jQuery, Lodash, etc.)",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
@@ -799,6 +851,10 @@ func main() {
|
||||
"type": "string",
|
||||
"description": "Tab ID (optional, uses current tab)",
|
||||
},
|
||||
"inject_library": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Library to inject before executing command (optional). Can be a known library name (axe, jquery, lodash, moment, underscore) or a full URL",
|
||||
},
|
||||
"timeout": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Timeout in seconds (default: 5)",
|
||||
@@ -816,21 +872,52 @@ func main() {
|
||||
|
||||
command := getStringParam(params, "command", "")
|
||||
tab := getStringParam(params, "tab", cremoteServer.currentTab)
|
||||
injectLibrary := getStringParam(params, "inject_library", "")
|
||||
timeout := getIntParam(params, "timeout", 5)
|
||||
|
||||
if command == "" {
|
||||
return nil, fmt.Errorf("command parameter is required")
|
||||
}
|
||||
|
||||
// Execute console command using existing EvalJS functionality
|
||||
result, err := cremoteServer.client.EvalJS(tab, command, timeout)
|
||||
// Build command parameters
|
||||
cmdParams := map[string]string{
|
||||
"command": command,
|
||||
"timeout": strconv.Itoa(timeout),
|
||||
}
|
||||
if tab != "" {
|
||||
cmdParams["tab"] = tab
|
||||
}
|
||||
if injectLibrary != "" {
|
||||
cmdParams["inject_library"] = injectLibrary
|
||||
}
|
||||
|
||||
// Send command to daemon
|
||||
resp, err := cremoteServer.client.SendCommand("console-command", cmdParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute console command: %w", err)
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Console command failed: %s", resp.Error)),
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Format result
|
||||
resultStr := fmt.Sprintf("Console command executed successfully.\nCommand: %s", command)
|
||||
if injectLibrary != "" {
|
||||
resultStr += fmt.Sprintf("\nInjected library: %s", injectLibrary)
|
||||
}
|
||||
if resp.Data != nil {
|
||||
resultStr += fmt.Sprintf("\nResult: %v", resp.Data)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Console command executed successfully.\nCommand: %s\nResult: %s", command, result)),
|
||||
mcp.NewTextContent(resultStr),
|
||||
},
|
||||
IsError: false,
|
||||
}, nil
|
||||
@@ -1989,7 +2076,7 @@ func main() {
|
||||
// Accessibility tree tools
|
||||
mcpServer.AddTool(mcp.Tool{
|
||||
Name: "get_accessibility_tree_cremotemcp",
|
||||
Description: "Get the full accessibility tree for a page or with limited depth",
|
||||
Description: "Get the full accessibility tree for a page with optional contrast data annotation",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
@@ -2002,6 +2089,11 @@ func main() {
|
||||
"description": "Maximum depth to retrieve (optional, omit for full tree)",
|
||||
"minimum": 0,
|
||||
},
|
||||
"include_contrast": map[string]any{
|
||||
"type": "boolean",
|
||||
"description": "Include contrast check availability annotation for text nodes (default: false)",
|
||||
"default": false,
|
||||
},
|
||||
"timeout": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Timeout in seconds",
|
||||
@@ -2017,6 +2109,7 @@ func main() {
|
||||
}
|
||||
|
||||
tab := getStringParam(params, "tab", cremoteServer.currentTab)
|
||||
includeContrast := getBoolParam(params, "include_contrast", false)
|
||||
timeout := getIntParam(params, "timeout", 5)
|
||||
|
||||
// Parse depth parameter
|
||||
@@ -2025,12 +2118,36 @@ func main() {
|
||||
depth = &depthParam
|
||||
}
|
||||
|
||||
result, err := cremoteServer.client.GetAccessibilityTree(tab, depth, timeout)
|
||||
// Build command parameters
|
||||
cmdParams := map[string]string{
|
||||
"timeout": strconv.Itoa(timeout),
|
||||
}
|
||||
if tab != "" {
|
||||
cmdParams["tab"] = tab
|
||||
}
|
||||
if depth != nil {
|
||||
cmdParams["depth"] = strconv.Itoa(*depth)
|
||||
}
|
||||
if includeContrast {
|
||||
cmdParams["include_contrast"] = "true"
|
||||
}
|
||||
|
||||
// Send command to daemon
|
||||
resp, err := cremoteServer.client.SendCommand("get-accessibility-tree", cmdParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get accessibility tree: %w", err)
|
||||
}
|
||||
|
||||
resultJSON, err := json.MarshalIndent(result, "", " ")
|
||||
if !resp.Success {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Failed to get accessibility tree: %s", resp.Error)),
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
resultJSON, err := json.MarshalIndent(resp.Data, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal result: %w", err)
|
||||
}
|
||||
@@ -3303,6 +3420,607 @@ func main() {
|
||||
}, nil
|
||||
})
|
||||
|
||||
// Register web_inject_axe tool
|
||||
mcpServer.AddTool(mcp.Tool{
|
||||
Name: "web_inject_axe_cremotemcp",
|
||||
Description: "Inject axe-core accessibility testing library into the page for comprehensive WCAG 2.1 AA/AAA testing",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"tab": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Tab ID (optional, uses current tab)",
|
||||
},
|
||||
"version": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Axe-core version (optional, defaults to 4.8.0)",
|
||||
"default": "4.8.0",
|
||||
},
|
||||
"timeout": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Timeout in seconds (default: 10)",
|
||||
"default": 10,
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
}, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
params, ok := request.Params.Arguments.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid arguments format")
|
||||
}
|
||||
|
||||
tab := getStringParam(params, "tab", cremoteServer.currentTab)
|
||||
version := getStringParam(params, "version", "4.8.0")
|
||||
timeout := getIntParam(params, "timeout", 10)
|
||||
|
||||
err := cremoteServer.client.InjectAxeCore(tab, version, timeout)
|
||||
if err != nil {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Failed to inject axe-core: %v", err)),
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Successfully injected axe-core v%s", version)),
|
||||
},
|
||||
IsError: false,
|
||||
}, nil
|
||||
})
|
||||
|
||||
// Register web_run_axe tool
|
||||
mcpServer.AddTool(mcp.Tool{
|
||||
Name: "web_run_axe_cremotemcp",
|
||||
Description: "Run axe-core accessibility tests and return violations, passes, incomplete checks, and inapplicable rules",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"tab": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Tab ID (optional, uses current tab)",
|
||||
},
|
||||
"run_only": map[string]any{
|
||||
"type": "array",
|
||||
"description": "Array of tags to run (e.g., ['wcag2a', 'wcag2aa', 'wcag21aa'])",
|
||||
"items": map[string]any{
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"rules": map[string]any{
|
||||
"type": "object",
|
||||
"description": "Specific rules configuration (optional)",
|
||||
},
|
||||
"timeout": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Timeout in seconds (default: 30)",
|
||||
"default": 30,
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
}, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
params, ok := request.Params.Arguments.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid arguments format")
|
||||
}
|
||||
|
||||
tab := getStringParam(params, "tab", cremoteServer.currentTab)
|
||||
timeout := getIntParam(params, "timeout", 30)
|
||||
|
||||
// Build options
|
||||
options := make(map[string]interface{})
|
||||
if runOnly, ok := params["run_only"].([]interface{}); ok && len(runOnly) > 0 {
|
||||
tags := make([]string, 0, len(runOnly))
|
||||
for _, tag := range runOnly {
|
||||
if tagStr, ok := tag.(string); ok {
|
||||
tags = append(tags, tagStr)
|
||||
}
|
||||
}
|
||||
if len(tags) > 0 {
|
||||
options["runOnly"] = map[string]interface{}{
|
||||
"type": "tag",
|
||||
"values": tags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rules, ok := params["rules"].(map[string]interface{}); ok && len(rules) > 0 {
|
||||
options["rules"] = rules
|
||||
}
|
||||
|
||||
result, err := cremoteServer.client.RunAxeCore(tab, options, timeout)
|
||||
if err != nil {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Failed to run axe-core: %v", err)),
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Format results as JSON
|
||||
resultJSON, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal results: %w", err)
|
||||
}
|
||||
|
||||
// Create summary
|
||||
summary := fmt.Sprintf("Axe-core Accessibility Test Results:\n\n"+
|
||||
"URL: %s\n"+
|
||||
"Timestamp: %s\n"+
|
||||
"Test Engine: %s v%s\n\n"+
|
||||
"Summary:\n"+
|
||||
" Violations: %d\n"+
|
||||
" Passes: %d\n"+
|
||||
" Incomplete: %d (require manual review)\n"+
|
||||
" Inapplicable: %d\n\n"+
|
||||
"Full Results:\n%s",
|
||||
result.URL,
|
||||
result.Timestamp,
|
||||
result.TestEngine.Name,
|
||||
result.TestEngine.Version,
|
||||
len(result.Violations),
|
||||
len(result.Passes),
|
||||
len(result.Incomplete),
|
||||
len(result.Inapplicable),
|
||||
string(resultJSON))
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(summary),
|
||||
},
|
||||
IsError: false,
|
||||
}, nil
|
||||
})
|
||||
|
||||
// Register web_contrast_check tool
|
||||
mcpServer.AddTool(mcp.Tool{
|
||||
Name: "web_contrast_check_cremotemcp",
|
||||
Description: "Check color contrast ratios for text elements and verify WCAG AA/AAA compliance",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"tab": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Tab ID (optional, uses current tab)",
|
||||
},
|
||||
"selector": map[string]any{
|
||||
"type": "string",
|
||||
"description": "CSS selector for specific elements (optional, defaults to all text elements)",
|
||||
},
|
||||
"timeout": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Timeout in seconds (default: 10)",
|
||||
"default": 10,
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
}, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
params, ok := request.Params.Arguments.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid arguments format")
|
||||
}
|
||||
|
||||
tab := getStringParam(params, "tab", cremoteServer.currentTab)
|
||||
selector := getStringParam(params, "selector", "")
|
||||
timeout := getIntParam(params, "timeout", 10)
|
||||
|
||||
result, err := cremoteServer.client.CheckContrast(tab, selector, timeout)
|
||||
if err != nil {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Failed to check contrast: %v", err)),
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Format results as JSON
|
||||
resultJSON, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal results: %w", err)
|
||||
}
|
||||
|
||||
// Create summary with violations
|
||||
summary := fmt.Sprintf("Color Contrast Check Results:\n\n"+
|
||||
"Summary:\n"+
|
||||
" Total Elements Checked: %d\n"+
|
||||
" WCAG AA Compliance:\n"+
|
||||
" Passed: %d\n"+
|
||||
" Failed: %d\n"+
|
||||
" WCAG AAA Compliance:\n"+
|
||||
" Passed: %d\n"+
|
||||
" Failed: %d\n"+
|
||||
" Unable to Check: %d\n\n",
|
||||
result.TotalElements,
|
||||
result.PassedAA,
|
||||
result.FailedAA,
|
||||
result.PassedAAA,
|
||||
result.FailedAAA,
|
||||
result.UnableToCheck)
|
||||
|
||||
// Add violations if any
|
||||
if result.FailedAA > 0 {
|
||||
summary += "WCAG AA Violations:\n"
|
||||
for _, elem := range result.Elements {
|
||||
if !elem.PassesAA && elem.Error == "" {
|
||||
summary += fmt.Sprintf(" - %s: %.2f:1 (required: %.1f:1)\n"+
|
||||
" Text: %s\n"+
|
||||
" Colors: %s on %s\n",
|
||||
elem.Selector,
|
||||
elem.ContrastRatio,
|
||||
elem.RequiredAA,
|
||||
elem.Text,
|
||||
elem.ForegroundColor,
|
||||
elem.BackgroundColor)
|
||||
}
|
||||
}
|
||||
summary += "\n"
|
||||
}
|
||||
|
||||
summary += fmt.Sprintf("Full Results:\n%s", string(resultJSON))
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(summary),
|
||||
},
|
||||
IsError: false,
|
||||
}, nil
|
||||
})
|
||||
|
||||
// Register web_keyboard_test tool
|
||||
mcpServer.AddTool(mcp.Tool{
|
||||
Name: "web_keyboard_test_cremotemcp",
|
||||
Description: "Test keyboard navigation and accessibility including tab order, focus indicators, and keyboard traps",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"tab": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Tab ID (optional, uses current tab)",
|
||||
},
|
||||
"timeout": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Timeout in seconds (default: 15)",
|
||||
"default": 15,
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
}, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
params, ok := request.Params.Arguments.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid arguments format")
|
||||
}
|
||||
|
||||
tab := getStringParam(params, "tab", cremoteServer.currentTab)
|
||||
timeout := getIntParam(params, "timeout", 15)
|
||||
|
||||
result, err := cremoteServer.client.TestKeyboardNavigation(tab, timeout)
|
||||
if err != nil {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Failed to test keyboard navigation: %v", err)),
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Format results as JSON
|
||||
resultJSON, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal results: %w", err)
|
||||
}
|
||||
|
||||
// Create summary with issues
|
||||
summary := fmt.Sprintf("Keyboard Navigation Test Results:\n\n"+
|
||||
"Summary:\n"+
|
||||
" Total Interactive Elements: %d\n"+
|
||||
" Keyboard Focusable: %d\n"+
|
||||
" Not Focusable: %d\n"+
|
||||
" Missing Focus Indicator: %d\n"+
|
||||
" Keyboard Traps Detected: %d\n"+
|
||||
" Total Issues: %d\n\n",
|
||||
result.TotalInteractive,
|
||||
result.Focusable,
|
||||
result.NotFocusable,
|
||||
result.NoFocusIndicator,
|
||||
result.KeyboardTraps,
|
||||
len(result.Issues))
|
||||
|
||||
// Add high severity issues
|
||||
highSeverityIssues := 0
|
||||
for _, issue := range result.Issues {
|
||||
if issue.Severity == "high" {
|
||||
highSeverityIssues++
|
||||
}
|
||||
}
|
||||
|
||||
if highSeverityIssues > 0 {
|
||||
summary += fmt.Sprintf("High Severity Issues (%d):\n", highSeverityIssues)
|
||||
count := 0
|
||||
for _, issue := range result.Issues {
|
||||
if issue.Severity == "high" && count < 10 {
|
||||
summary += fmt.Sprintf(" - %s: %s\n Element: %s\n",
|
||||
issue.Type,
|
||||
issue.Description,
|
||||
issue.Element)
|
||||
count++
|
||||
}
|
||||
}
|
||||
if highSeverityIssues > 10 {
|
||||
summary += fmt.Sprintf(" ... and %d more issues\n", highSeverityIssues-10)
|
||||
}
|
||||
summary += "\n"
|
||||
}
|
||||
|
||||
// Add tab order summary
|
||||
if len(result.TabOrder) > 0 {
|
||||
summary += fmt.Sprintf("Tab Order (first 5 elements):\n")
|
||||
for i, elem := range result.TabOrder {
|
||||
if i >= 5 {
|
||||
summary += fmt.Sprintf(" ... and %d more elements\n", len(result.TabOrder)-5)
|
||||
break
|
||||
}
|
||||
focusIndicator := "✓"
|
||||
if !elem.HasFocusStyle {
|
||||
focusIndicator = "✗"
|
||||
}
|
||||
summary += fmt.Sprintf(" %d. %s [%s] %s - Focus: %s\n",
|
||||
elem.Index+1,
|
||||
elem.Selector,
|
||||
elem.TagName,
|
||||
elem.Text,
|
||||
focusIndicator)
|
||||
}
|
||||
summary += "\n"
|
||||
}
|
||||
|
||||
summary += fmt.Sprintf("Full Results:\n%s", string(resultJSON))
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(summary),
|
||||
},
|
||||
IsError: false,
|
||||
}, nil
|
||||
})
|
||||
|
||||
// Register web_zoom_test tool
|
||||
mcpServer.AddTool(mcp.Tool{
|
||||
Name: "web_zoom_test_cremotemcp",
|
||||
Description: "Test page at different zoom levels (100%, 200%, 400%) and verify WCAG 1.4.4 and 1.4.10 compliance",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"tab": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Tab ID (optional, uses current tab)",
|
||||
},
|
||||
"zoom_levels": map[string]any{
|
||||
"type": "array",
|
||||
"description": "Array of zoom levels to test (optional, defaults to [1.0, 2.0, 4.0])",
|
||||
"items": map[string]any{
|
||||
"type": "number",
|
||||
},
|
||||
},
|
||||
"timeout": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Timeout in seconds per zoom level (default: 10)",
|
||||
"default": 10,
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
}, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
params, ok := request.Params.Arguments.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid arguments format")
|
||||
}
|
||||
|
||||
tab := getStringParam(params, "tab", cremoteServer.currentTab)
|
||||
timeout := getIntParam(params, "timeout", 10)
|
||||
|
||||
// Parse zoom levels if provided
|
||||
var zoomLevels []float64
|
||||
if zoomLevelsParam, ok := params["zoom_levels"].([]interface{}); ok {
|
||||
for _, level := range zoomLevelsParam {
|
||||
if levelFloat, ok := level.(float64); ok {
|
||||
zoomLevels = append(zoomLevels, levelFloat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result, err := cremoteServer.client.TestZoom(tab, zoomLevels, timeout)
|
||||
if err != nil {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Failed to test zoom: %v", err)),
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Format results as JSON
|
||||
resultJSON, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal results: %w", err)
|
||||
}
|
||||
|
||||
// Create summary
|
||||
summary := fmt.Sprintf("Zoom Level Test Results:\n\n"+
|
||||
"Tested %d zoom levels\n"+
|
||||
"Total Issues: %d\n\n",
|
||||
len(result.ZoomLevels),
|
||||
len(result.Issues))
|
||||
|
||||
// Add zoom level details
|
||||
for _, zoomTest := range result.ZoomLevels {
|
||||
status := "✓ PASS"
|
||||
if zoomTest.HasHorizontalScroll || zoomTest.OverflowingElements > 0 || !zoomTest.TextReadable {
|
||||
status = "✗ FAIL"
|
||||
}
|
||||
|
||||
summary += fmt.Sprintf("Zoom %.0f%% %s:\n"+
|
||||
" Viewport: %dx%d\n"+
|
||||
" Content: %dx%d\n"+
|
||||
" Horizontal Scroll: %v\n"+
|
||||
" Overflowing Elements: %d\n"+
|
||||
" Text Readable: %v\n\n",
|
||||
zoomTest.ZoomLevel*100,
|
||||
status,
|
||||
zoomTest.ViewportWidth,
|
||||
zoomTest.ViewportHeight,
|
||||
zoomTest.ContentWidth,
|
||||
zoomTest.ContentHeight,
|
||||
zoomTest.HasHorizontalScroll,
|
||||
zoomTest.OverflowingElements,
|
||||
zoomTest.TextReadable)
|
||||
}
|
||||
|
||||
// Add issues
|
||||
if len(result.Issues) > 0 {
|
||||
summary += "Issues Found:\n"
|
||||
for _, issue := range result.Issues {
|
||||
summary += fmt.Sprintf(" [%.0f%%] %s (%s): %s\n",
|
||||
issue.ZoomLevel*100,
|
||||
issue.Type,
|
||||
issue.Severity,
|
||||
issue.Description)
|
||||
}
|
||||
summary += "\n"
|
||||
}
|
||||
|
||||
summary += fmt.Sprintf("Full Results:\n%s", string(resultJSON))
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(summary),
|
||||
},
|
||||
IsError: false,
|
||||
}, nil
|
||||
})
|
||||
|
||||
// Register web_reflow_test tool
|
||||
mcpServer.AddTool(mcp.Tool{
|
||||
Name: "web_reflow_test_cremotemcp",
|
||||
Description: "Test responsive design at WCAG breakpoints (320px, 1280px) and verify WCAG 1.4.10 reflow compliance",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"tab": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Tab ID (optional, uses current tab)",
|
||||
},
|
||||
"widths": map[string]any{
|
||||
"type": "array",
|
||||
"description": "Array of viewport widths to test in pixels (optional, defaults to [320, 1280])",
|
||||
"items": map[string]any{
|
||||
"type": "integer",
|
||||
},
|
||||
},
|
||||
"timeout": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Timeout in seconds per width (default: 10)",
|
||||
"default": 10,
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
}, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
params, ok := request.Params.Arguments.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid arguments format")
|
||||
}
|
||||
|
||||
tab := getStringParam(params, "tab", cremoteServer.currentTab)
|
||||
timeout := getIntParam(params, "timeout", 10)
|
||||
|
||||
// Parse widths if provided
|
||||
var widths []int
|
||||
if widthsParam, ok := params["widths"].([]interface{}); ok {
|
||||
for _, width := range widthsParam {
|
||||
if widthFloat, ok := width.(float64); ok {
|
||||
widths = append(widths, int(widthFloat))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result, err := cremoteServer.client.TestReflow(tab, widths, timeout)
|
||||
if err != nil {
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("Failed to test reflow: %v", err)),
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Format results as JSON
|
||||
resultJSON, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal results: %w", err)
|
||||
}
|
||||
|
||||
// Create summary
|
||||
summary := fmt.Sprintf("Reflow/Responsive Design Test Results:\n\n"+
|
||||
"Tested %d breakpoints\n"+
|
||||
"Total Issues: %d\n\n",
|
||||
len(result.Breakpoints),
|
||||
len(result.Issues))
|
||||
|
||||
// Add breakpoint details
|
||||
for _, breakpoint := range result.Breakpoints {
|
||||
status := "✓ PASS"
|
||||
if breakpoint.HasHorizontalScroll || !breakpoint.ResponsiveLayout || breakpoint.OverflowingElements > 0 {
|
||||
status = "✗ FAIL"
|
||||
}
|
||||
|
||||
summary += fmt.Sprintf("%dpx Width %s:\n"+
|
||||
" Viewport: %dx%d\n"+
|
||||
" Content: %dx%d\n"+
|
||||
" Horizontal Scroll: %v\n"+
|
||||
" Responsive Layout: %v\n"+
|
||||
" Overflowing Elements: %d\n\n",
|
||||
breakpoint.Width,
|
||||
status,
|
||||
breakpoint.Width,
|
||||
breakpoint.Height,
|
||||
breakpoint.ContentWidth,
|
||||
breakpoint.ContentHeight,
|
||||
breakpoint.HasHorizontalScroll,
|
||||
breakpoint.ResponsiveLayout,
|
||||
breakpoint.OverflowingElements)
|
||||
}
|
||||
|
||||
// Add issues
|
||||
if len(result.Issues) > 0 {
|
||||
summary += "Issues Found:\n"
|
||||
for _, issue := range result.Issues {
|
||||
summary += fmt.Sprintf(" [%dpx] %s (%s): %s\n",
|
||||
issue.Width,
|
||||
issue.Type,
|
||||
issue.Severity,
|
||||
issue.Description)
|
||||
}
|
||||
summary += "\n"
|
||||
}
|
||||
|
||||
summary += fmt.Sprintf("Full Results:\n%s", string(resultJSON))
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(summary),
|
||||
},
|
||||
IsError: false,
|
||||
}, nil
|
||||
})
|
||||
|
||||
// Start the server
|
||||
log.Printf("Cremote MCP server ready")
|
||||
if err := server.ServeStdio(mcpServer); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user