Files
hexis/internal/provider/workspace_mcp_test.go
T
kami 945e4ba1ac Surface workspace-mcp tool-handler errors as execution failures
Decision: workspace-mcp's /api/tool, /api/tools direct-HTTP endpoints are
a first-class, server-sanctioned transport (see transport.py's "Direct
HTTP API for non-MCP clients"), not a workaround — Hexis's REST provider
is the right integration, no migration to the SSE/JSON-RPC MCP transport
needed.

The actual gap: workspace-mcp catches tool-handler exceptions and reports
them as an ERROR-coded warning inside a 200 response envelope rather than
an HTTP error status, so Execute()'s status-code check alone let every
such failure through as a successful execution. Detect the ERROR-coded
warning and return it as an error so it maps to Hexis's failed status.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ghELqYhZNLub2TXGMazqA
2026-07-20 11:30:42 +04:00

60 lines
1.7 KiB
Go

package provider
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/kami/hexis/internal/domain"
)
func newWorkspaceTestServer(t *testing.T, status int, body map[string]any) (*httptest.Server, *WorkspaceMCPProvider) {
t.Helper()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(status)
json.NewEncoder(w).Encode(body)
}))
t.Cleanup(srv.Close)
allowlist := ToolAllowlist{Tools: map[string]ToolMapping{
"workspace_tool": {Capability: "ws.do_thing", Risk: "write", ReadOnly: false},
}}
return srv, NewWorkspaceMCPProvider(srv.URL, allowlist)
}
func TestExecute_SucceedsOnPlainResult(t *testing.T) {
_, p := newWorkspaceTestServer(t, http.StatusOK, map[string]any{
"summary": "ok", "items": []any{}, "warnings": []any{}, "truncated": false,
})
cap := &domain.Capability{Name: "ws.do_thing"}
result, err := p.Execute(cap, &domain.ExecuteRequest{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if result == nil {
t.Fatal("expected non-nil result")
}
}
// TestExecute_ErrorEnvelopeReportedAsFailure verifies that a 200 response
// carrying workspace-mcp's ERROR-coded warning envelope (its convention for
// tool-handler exceptions) is treated as a failed execution, not a success.
func TestExecute_ErrorEnvelopeReportedAsFailure(t *testing.T) {
_, p := newWorkspaceTestServer(t, http.StatusOK, map[string]any{
"summary": "boom",
"items": []any{},
"warnings": []any{
map[string]any{"code": "ERROR", "message": "boom"},
},
"truncated": false,
})
cap := &domain.Capability{Name: "ws.do_thing"}
_, err := p.Execute(cap, &domain.ExecuteRequest{})
if err == nil {
t.Fatal("expected error for ERROR-coded warning envelope, got nil")
}
}