claudetool/browse: block port 80 access in browser tools

Prevents agents from attempting to browse to port 80, which is reserved
for the Sketch server itself. This is a common source of confusion.

Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: sf481df655a16903ek
diff --git a/claudetool/browse/browse.go b/claudetool/browse/browse.go
index 9c2cca1..dfb963e 100644
--- a/claudetool/browse/browse.go
+++ b/claudetool/browse/browse.go
@@ -8,6 +8,7 @@
 	"fmt"
 	"log"
 	"net/http"
+	"net/url"
 	"os"
 	"path/filepath"
 	"strings"
@@ -148,7 +149,19 @@
 type navigateInput struct {
 	URL     string `json:"url"`
 	Timeout string `json:"timeout,omitempty"`
-} // NewNavigateTool creates a tool for navigating to URLs
+}
+
+// isPort80 reports whether urlStr definitely uses port 80.
+func isPort80(urlStr string) bool {
+	parsedURL, err := url.Parse(urlStr)
+	if err != nil {
+		return false
+	}
+	port := parsedURL.Port()
+	return port == "80" || (port == "" && parsedURL.Scheme == "http")
+}
+
+// NewNavigateTool creates a tool for navigating to URLs
 func (b *BrowseTools) NewNavigateTool() *llm.Tool {
 	return &llm.Tool{
 		Name:        "browser_navigate",
@@ -177,6 +190,10 @@
 		return llm.TextContent(errorResponse(fmt.Errorf("invalid input: %w", err))), nil
 	}
 
+	if isPort80(input.URL) {
+		return llm.TextContent(errorResponse(fmt.Errorf("port 80 is not the port you're looking for--it is the main sketch server"))), nil
+	}
+
 	browserCtx, err := b.GetBrowserContext()
 	if err != nil {
 		return llm.TextContent(errorResponse(err)), nil