fix(mcp): harden dashboard dispatch lifecycle

Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
naiba
2026-07-20 04:32:16 +00:00
co-authored by naiba/CloudCode
parent 2640b86d3a
commit 3f6de265a0
48 changed files with 1332 additions and 265 deletions
@@ -0,0 +1,47 @@
package controller
import (
"encoding/json"
"strings"
"sync"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
"github.com/nezhahq/nezha/model"
)
var registerUnmarshalableMCPTool sync.Once
func TestMCPToolCall_unmarshalableSuccessResultReturnsExplicitToolError(t *testing.T) {
// Given
cleanup, uid := setupMCPTest(t)
defer cleanup()
registerUnmarshalableMCPTool.Do(func() {
registerMCPTool(&mcpTool{
Name: "test.unmarshalable-success",
RequiredScope: "",
Handler: func(*gin.Context, json.RawMessage) (any, error) {
return map[string]any{"unsupported": func() {}}, nil
},
})
})
tok, plainToken := mkToken(t, uid, []string{model.ScopeServerRead}, nil)
require.NotEmpty(t, plainToken)
c, w := mcpCallCtx(t, tok, uid, jsonRPCRequest{
JSONRPC: "2.0", ID: json.RawMessage("1"), Method: "tools/call",
Params: jsonObj(t, toolCallParams{Name: "test.unmarshalable-success", Arguments: json.RawMessage("{}")}),
})
// When
mcpEndpoint(c)
// Then
_, result := decodeRPC(w)
require.NotNil(t, result)
require.True(t, result.IsError)
require.Nil(t, result.StructuredContent)
require.Len(t, result.Content, 1)
require.True(t, strings.Contains(result.Content[0].Text, "encode"), result.Content[0].Text)
}