fix(mcp): preserve structured plugin tool errors (#3815)

This commit is contained in:
Carlos Florêncio
2026-09-19 12:57:25 +01:00
committed by GitHub
parent e3ed7bfd5d
commit f296d44f84
2 changed files with 65 additions and 1 deletions
@@ -0,0 +1,60 @@
package mcp
import (
"encoding/json"
"testing"
"github.com/kandev/kandev/internal/mcp/plugintools"
mcplib "github.com/mark3labs/mcp-go/mcp"
"github.com/stretchr/testify/require"
)
func TestPluginToolPreservesStructuredResultAndErrorStatus(t *testing.T) {
for _, isError := range []bool{false, true} {
name := "success"
if isError {
name = "error"
}
t.Run(name, func(t *testing.T) {
structured := map[string]any{"status": "uncertain", "recipient": "U01234567", "duplicate": true}
backend := &testBackend{response: map[string]any{
"text": "Check delivery before retrying.", "structured_content": structured, "is_error": isError,
}}
server := New(backend, "session-1", "task-1", 10005, newTestLogger(t), "", false, ModeTask)
definition := plugintools.Definition{
PluginID: "notifications", LocalName: "notify", ExposedName: plugintools.ExposedName("notifications", "notify"),
Description: "Notify a user", InputSchema: []byte(`{"type":"object"}`),
Surfaces: []string{plugintools.SurfaceKanban},
}
require.NoError(t, server.SetPluginTools(plugintools.Snapshot{
Generation: "g", Revision: 1, Tools: []plugintools.Definition{definition},
}))
result := callTool(t, server, definition.ExposedName, map[string]any{})
require.Equal(t, isError, result.IsError)
require.Equal(t, structured, result.StructuredContent)
require.Len(t, result.Content, 1)
require.Equal(t, "Check delivery before retrying.", result.Content[0].(mcplib.TextContent).Text)
})
}
}
func TestPluginToolErrorOmitsNilStructuredContent(t *testing.T) {
backend := &testBackend{response: map[string]any{
"text": "Check delivery before retrying.", "structured_content": nil, "is_error": true,
}}
server := New(backend, "session-1", "task-1", 10005, newTestLogger(t), "", false, ModeTask)
definition := plugintools.Definition{
PluginID: "notifications", LocalName: "notify", ExposedName: plugintools.ExposedName("notifications", "notify"),
Description: "Notify a user", InputSchema: []byte(`{"type":"object"}`),
Surfaces: []string{plugintools.SurfaceKanban},
}
require.NoError(t, server.SetPluginTools(plugintools.Snapshot{
Generation: "g", Revision: 1, Tools: []plugintools.Definition{definition},
}))
result := callTool(t, server, definition.ExposedName, map[string]any{})
serialized, err := json.Marshal(result)
require.NoError(t, err)
require.NotContains(t, string(serialized), `"structuredContent"`)
}
+5 -1
View File
@@ -989,7 +989,11 @@ func (s *Server) registerPluginTools() {
return nil, err
}
if result.IsError {
return mcp.NewToolResultError(result.Text), nil
response := mcp.NewToolResultError(result.Text)
if result.StructuredContent != nil {
response.StructuredContent = result.StructuredContent
}
return response, nil
}
if result.StructuredContent != nil {
return mcp.NewToolResultStructured(result.StructuredContent, result.Text), nil