ada tools update

This commit is contained in:
Josh at WLTechBlog
2025-10-02 11:40:26 -05:00
parent 2ef7512918
commit 2817b8a049
11 changed files with 6010 additions and 180 deletions

View File

@@ -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 {