This commit is contained in:
Josh at WLTechBlog
2025-10-16 10:54:37 -05:00
parent ccd8c77a3e
commit 4d55acca95
45 changed files with 8499 additions and 2416 deletions

View File

@@ -6,7 +6,9 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"git.teamworkapps.com/shortcut/cremote/client"
@@ -189,7 +191,7 @@ func main() {
// Register web_interact tool
mcpServer.AddTool(mcp.Tool{
Name: "web_interact_cremotemcp",
Description: "Interact with web elements (click, fill, submit)",
Description: "Interact with web elements (click, fill, submit, upload). For upload action, automatically handles file transfer from host to container if needed.",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]any{
@@ -204,7 +206,7 @@ func main() {
},
"value": map[string]any{
"type": "string",
"description": "Value to fill (for fill/upload actions)",
"description": "Value to fill (for fill/upload actions). For upload, can be either a local host path or container path - will auto-detect and transfer if needed.",
},
"tab": map[string]any{
"type": "string",
@@ -264,8 +266,31 @@ func main() {
if value == "" {
return nil, fmt.Errorf("value parameter (file path) is required for upload action")
}
err = cremoteServer.client.UploadFile(tab, selector, value, timeout)
message = fmt.Sprintf("Uploaded file %s to element %s", value, selector)
// Auto-detect if file needs to be transferred to container
// If the path doesn't start with /tmp/ or other container paths, assume it's a host path
containerPath := value
var transferredFile bool
// Check if file exists in container - if not, try to upload from host
if !strings.HasPrefix(value, "/tmp/") && !strings.HasPrefix(value, "/var/") {
// This looks like a host path, try to upload to container
uploadedPath, uploadErr := cremoteServer.client.UploadFileToContainer(value, "")
if uploadErr == nil {
containerPath = uploadedPath
transferredFile = true
} else {
// If upload fails, maybe it's already a valid container path, try to use it as-is
// The actual upload to the form will fail if the file doesn't exist
}
}
err = cremoteServer.client.UploadFile(tab, selector, containerPath, timeout)
if transferredFile {
message = fmt.Sprintf("Transferred file from host (%s) to container (%s) and uploaded to element %s", value, containerPath, selector)
} else {
message = fmt.Sprintf("Uploaded file %s to element %s", containerPath, selector)
}
case "select":
if value == "" {
@@ -380,13 +405,13 @@ func main() {
// Register web_screenshot tool
mcpServer.AddTool(mcp.Tool{
Name: "web_screenshot_cremotemcp",
Description: "Take a screenshot of the current page with optional zoom level and viewport size for accessibility testing",
Description: "Take a screenshot of the current page with optional zoom level and viewport size for accessibility testing. Automatically downloads the screenshot from container to host.",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]any{
"output": map[string]any{
"type": "string",
"description": "Output file path",
"description": "Output file path on the host machine (e.g., /home/user/screenshots/page.png). The screenshot will be automatically downloaded from the container.",
},
"full_page": map[string]any{
"type": "boolean",
@@ -449,9 +474,23 @@ func main() {
return nil, fmt.Errorf("no tab available - navigate to a page first")
}
// Build command parameters
// Determine container path and host path
var containerPath, hostPath string
// If output path starts with /tmp/ or /var/, treat it as container path
if strings.HasPrefix(output, "/tmp/") || strings.HasPrefix(output, "/var/") {
containerPath = output
// Generate a default host path
hostPath = "/tmp/" + filepath.Base(output)
} else {
// Treat as host path, generate container path
hostPath = output
containerPath = "/tmp/screenshot-" + strconv.FormatInt(time.Now().Unix(), 10) + filepath.Ext(output)
}
// Build command parameters for taking screenshot in container
cmdParams := map[string]string{
"output": output,
"output": containerPath,
"full-page": strconv.FormatBool(fullPage),
"tab": tab,
"timeout": strconv.Itoa(timeout),
@@ -466,7 +505,7 @@ func main() {
cmdParams["height"] = strconv.Itoa(height)
}
// Send command to daemon
// Send command to daemon to take screenshot
resp, err := cremoteServer.client.SendCommand("screenshot", cmdParams)
if err != nil {
return nil, fmt.Errorf("failed to take screenshot: %w", err)
@@ -481,11 +520,22 @@ func main() {
}, nil
}
cremoteServer.screenshots = append(cremoteServer.screenshots, output)
// Automatically download the screenshot from container to host
err = cremoteServer.client.DownloadFileFromContainer(containerPath, hostPath)
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(fmt.Sprintf("Screenshot taken in container (%s) but failed to download to host: %v", containerPath, err)),
},
IsError: true,
}, nil
}
cremoteServer.screenshots = append(cremoteServer.screenshots, hostPath)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(fmt.Sprintf("Screenshot saved to %s", output)),
mcp.NewTextContent(fmt.Sprintf("Screenshot saved to %s (automatically downloaded from container)", hostPath)),
},
IsError: false,
}, nil
@@ -1766,7 +1816,7 @@ func main() {
// web_screenshot_element_cremotemcp - Screenshot specific elements
mcpServer.AddTool(mcp.Tool{
Name: "web_screenshot_element_cremotemcp",
Description: "Take a screenshot of a specific element on the page",
Description: "Take a screenshot of a specific element on the page. Automatically downloads the screenshot from container to host.",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]any{
@@ -1776,7 +1826,7 @@ func main() {
},
"output": map[string]any{
"type": "string",
"description": "Path where to save the screenshot",
"description": "Path where to save the screenshot on the host machine. The screenshot will be automatically downloaded from the container.",
},
"tab": map[string]any{
"type": "string",
@@ -1811,7 +1861,22 @@ func main() {
return nil, fmt.Errorf("no tab available - navigate to a page first")
}
err := cremoteServer.client.ScreenshotElement(tab, selector, output, timeout)
// Determine container path and host path
var containerPath, hostPath string
// If output path starts with /tmp/ or /var/, treat it as container path
if strings.HasPrefix(output, "/tmp/") || strings.HasPrefix(output, "/var/") {
containerPath = output
// Generate a default host path
hostPath = "/tmp/" + filepath.Base(output)
} else {
// Treat as host path, generate container path
hostPath = output
containerPath = "/tmp/screenshot-element-" + strconv.FormatInt(time.Now().Unix(), 10) + filepath.Ext(output)
}
// Take screenshot in container
err := cremoteServer.client.ScreenshotElement(tab, selector, containerPath, timeout)
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
@@ -1821,11 +1886,22 @@ func main() {
}, nil
}
cremoteServer.screenshots = append(cremoteServer.screenshots, output)
// Automatically download the screenshot from container to host
err = cremoteServer.client.DownloadFileFromContainer(containerPath, hostPath)
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(fmt.Sprintf("Screenshot taken in container (%s) but failed to download to host: %v", containerPath, err)),
},
IsError: true,
}, nil
}
cremoteServer.screenshots = append(cremoteServer.screenshots, hostPath)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(fmt.Sprintf("Element screenshot saved to: %s", output)),
mcp.NewTextContent(fmt.Sprintf("Element screenshot saved to %s (automatically downloaded from container)", hostPath)),
},
IsError: false,
}, nil
@@ -1834,13 +1910,13 @@ func main() {
// web_screenshot_enhanced_cremotemcp - Enhanced screenshots with metadata
mcpServer.AddTool(mcp.Tool{
Name: "web_screenshot_enhanced_cremotemcp",
Description: "Take an enhanced screenshot with metadata (timestamp, viewport size, URL)",
Description: "Take an enhanced screenshot with metadata (timestamp, viewport size, URL). Automatically downloads the screenshot from container to host.",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]any{
"output": map[string]any{
"type": "string",
"description": "Path where to save the screenshot",
"description": "Path where to save the screenshot on the host machine. The screenshot will be automatically downloaded from the container.",
},
"full_page": map[string]any{
"type": "boolean",
@@ -1876,7 +1952,22 @@ func main() {
return nil, fmt.Errorf("no tab available - navigate to a page first")
}
metadata, err := cremoteServer.client.ScreenshotEnhanced(tab, output, fullPage, timeout)
// Determine container path and host path
var containerPath, hostPath string
// If output path starts with /tmp/ or /var/, treat it as container path
if strings.HasPrefix(output, "/tmp/") || strings.HasPrefix(output, "/var/") {
containerPath = output
// Generate a default host path
hostPath = "/tmp/" + filepath.Base(output)
} else {
// Treat as host path, generate container path
hostPath = output
containerPath = "/tmp/screenshot-enhanced-" + strconv.FormatInt(time.Now().Unix(), 10) + filepath.Ext(output)
}
// Take enhanced screenshot in container
metadata, err := cremoteServer.client.ScreenshotEnhanced(tab, containerPath, fullPage, timeout)
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
@@ -1886,7 +1977,18 @@ func main() {
}, nil
}
cremoteServer.screenshots = append(cremoteServer.screenshots, output)
// Automatically download the screenshot from container to host
err = cremoteServer.client.DownloadFileFromContainer(containerPath, hostPath)
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(fmt.Sprintf("Screenshot taken in container (%s) but failed to download to host: %v", containerPath, err)),
},
IsError: true,
}, nil
}
cremoteServer.screenshots = append(cremoteServer.screenshots, hostPath)
metadataJSON, err := json.MarshalIndent(metadata, "", " ")
if err != nil {
@@ -1895,7 +1997,7 @@ func main() {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.NewTextContent(fmt.Sprintf("Enhanced screenshot saved with metadata:\n%s", string(metadataJSON))),
mcp.NewTextContent(fmt.Sprintf("Enhanced screenshot saved to %s (automatically downloaded from container) with metadata:\n%s", hostPath, string(metadataJSON))),
},
IsError: false,
}, nil