From 2046bc25134be278c962a3ca17f59f2e0020d807 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Fri, 6 Mar 2026 10:03:01 +0100 Subject: [PATCH] anvilmcp: fallback to default sandbox on permission errors When sandbox is not explicitly specified, retry with 'default' sandbox if the initial execution fails with permission denied or missing file errors. This allows third-party skills designed for execute_bash to work without modification while preserving isolation when sandbox is explicitly set. --- cmd/anvilmcp/execute.go | 9 +++++++++ cmd/anvilmcp/main.go | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/cmd/anvilmcp/execute.go b/cmd/anvilmcp/execute.go index e5bdbdd..4838a0d 100644 --- a/cmd/anvilmcp/execute.go +++ b/cmd/anvilmcp/execute.go @@ -85,6 +85,15 @@ func recordValidationFailure() { } } +func isPermissionError(err error) bool { + if err == nil { + return false + } + msg := strings.ToLower(err.Error()) + return strings.Contains(msg, "permission denied") || + strings.Contains(msg, "no such file or directory") +} + func validateCode(code string) error { if err := checkRateLimit(); err != nil { return err diff --git a/cmd/anvilmcp/main.go b/cmd/anvilmcp/main.go index 9234976..ba6e3de 100644 --- a/cmd/anvilmcp/main.go +++ b/cmd/anvilmcp/main.go @@ -134,6 +134,7 @@ func handleToolCall(req MCPRequest) { timeout = int(t) } sandbox, _ := params.Arguments["sandbox"].(string) + sandboxExplicit := sandbox != "" if sandbox == "" { sandbox = "anvilmcp" } @@ -145,6 +146,12 @@ func handleToolCall(req MCPRequest) { fmt.Fprintf(os.Stderr, "[anvilmcp] Executing bash code (timeout: %ds, sandbox: %s)\n", timeout, sandbox) result, err := executeCode(code, language, timeout, sandbox) + // Fallback to default sandbox on permission errors (only if sandbox wasn't explicit) + if err != nil && !sandboxExplicit && isPermissionError(err) { + fmt.Fprintf(os.Stderr, "[anvilmcp] Permission error in %s sandbox, falling back to default\n", sandbox) + result, err = executeCode(code, language, timeout, "default") + } + // Log token comparison codeTokens := estimateTokens(code) outputTokens := estimateTokens(result)