Skip to content

feat: add isError when error downstream response#301

Merged
iFurySt merged 9 commits intoAmoyLab:mainfrom
qiankunli:feat/set-isEror-when-error-downsteam-response
Feb 28, 2026
Merged

feat: add isError when error downstream response#301
iFurySt merged 9 commits intoAmoyLab:mainfrom
qiankunli:feat/set-isEror-when-error-downsteam-response

Conversation

@qiankunli
Copy link
Contributor

@qiankunli qiankunli commented Feb 24, 2026

Summary by Sourcery

Propagate HTTP error states from downstream responses into MCP tool results while improving MCP configuration unmarshalling errors.

New Features:

  • Add support for marking text-based CallTool results as errors when needed via a new constructor.

Enhancements:

  • Enrich MCPConfig JSON unmarshalling errors with contextual information including MCP name and tenant.
  • Update text response handler to flag tool results as errors when the downstream HTTP status code indicates a failure.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 24, 2026

Reviewer's Guide

Adds error-aware tool call results by marking downstream HTTP error responses in MCP results and improves MCPConfig JSON unmarshalling error messages with contextual wrapping.

Sequence diagram for TextHandler handling downstream HTTP response with isError flag

sequenceDiagram
    participant Client
    participant TextHandler
    participant HTTPClient as DownstreamHTTPService
    participant MCP as MCPResultFactory

    Client->>TextHandler: Handle(resp, tool, tmplConfig)
    TextHandler->>HTTPClient: Perform HTTP request
    HTTPClient-->>TextHandler: *http.Response resp

    alt TemplateConfigured
        TextHandler->>TextHandler: Render response body template
        TextHandler-->>TextHandler: rendered string
    else NoTemplate
        TextHandler-->>TextHandler: use raw response body as rendered
    end

    TextHandler->>TextHandler: isError = resp.StatusCode >= 400
    TextHandler->>MCP: NewCallToolResultTextWithError(rendered, isError)
    MCP-->>TextHandler: *CallToolResult (Content, IsError)

    TextHandler-->>Client: *CallToolResult
Loading

Class diagram for CallToolResult and error-aware text constructor

classDiagram
    class CallToolResult {
        []Content Content
        bool IsError
    }

    class Content {
    }

    class TextContent {
        string Type
        string Text
    }

    Content <|-- TextContent

    class MCPFactory {
        +NewCallToolResultText(text string) *CallToolResult
        +NewCallToolResultTextWithError(text string, isError bool) *CallToolResult
    }

    MCPFactory ..> CallToolResult
    MCPFactory ..> TextContent

    class TextHandler {
        +Handle(resp *http.Response, tool *ToolConfig, tmplConfig *TemplateConfig) (*CallToolResult, error)
    }

    TextHandler ..> MCPFactory
    TextHandler ..> CallToolResult

    class MCPConfigModel {
        string Name
        string Tenant
        string Routers
        string Servers
        string Tools
        string Prompts
        string McpServers
        +ToMCPConfig() (*MCPConfig, error)
    }

    class MCPConfig {
        string Name
        string Tenant
        []Router Routers
        []Server Servers
        []Tool Tools
        []Prompt Prompts
        []McpServer McpServers
    }

    MCPConfigModel ..> MCPConfig : ToMCPConfig
    MCPConfigModel ..> Router
    MCPConfigModel ..> Server
    MCPConfigModel ..> Tool
    MCPConfigModel ..> Prompt
    MCPConfigModel ..> McpServer

    class Router
    class Server
    class Tool
    class Prompt
    class McpServer

    class ErrorWrappingHelper {
        +wrapError(context string, err error) error
    }

    MCPConfigModel ..> ErrorWrappingHelper
Loading

File-Level Changes

Change Details Files
Improve MCPConfig JSON unmarshalling error diagnostics
  • Introduce a local helper to wrap JSON unmarshalling errors with MCP name and tenant context
  • Change all JSON unmarshal error returns in ToMCPConfig to use the contextual wrapper for Routers, Servers, Tools, Prompts, and McpServers fields
  • Tidy imports in the MCP storage model to group third-party and internal packages
internal/mcp/storage/model.go
Propagate downstream HTTP error state into MCP CallToolResult
  • Add a constructor that creates a CallToolResult with text content and an explicit IsError flag
  • Update TextHandler to set IsError based on HTTP response status code (>= 400) and use the new constructor
pkg/mcp/server_types.go
internal/core/handler.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In the new wrapError helper, the format string and argument order don’t match the intended message ("failed to unmarshal MCP %s configuration '%s' (tenant: '%s')" currently uses m.Name, m.Tenant, context but reads as if it should be m.Name, context, m.Tenant).
  • To avoid duplicating logic, consider having NewCallToolResultTextWithError delegate to NewCallToolResultText and then set IsError, instead of rebuilding the Content slice manually.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the new `wrapError` helper, the format string and argument order don’t match the intended message (`"failed to unmarshal MCP %s configuration '%s' (tenant: '%s')"` currently uses `m.Name, m.Tenant, context` but reads as if it should be `m.Name, context, m.Tenant`).
- To avoid duplicating logic, consider having `NewCallToolResultTextWithError` delegate to `NewCallToolResultText` and then set `IsError`, instead of rebuilding the `Content` slice manually.

## Individual Comments

### Comment 1
<location path="internal/mcp/storage/model.go" line_range="41" />
<code_context>
 	}

+	wrapError := func(context string, err error) error {
+		return fmt.Errorf("failed to unmarshal MCP %s configuration '%s' (tenant: '%s'): %w", m.Name, m.Tenant, context, err)
+	}
 	if len(m.Routers) > 0 {
</code_context>
<issue_to_address>
**issue (bug_risk):** The error message format string appears to mismatch the argument order, which can lead to confusing logs.

The format string `"failed to unmarshal MCP %s configuration '%s' (tenant: '%s'): %w"` with arguments `(m.Name, m.Tenant, context, err)` currently renders as “MCP <Name> configuration '<Tenant>' (tenant: '<context>')”, which mislabels the tenant/context. Please reorder placeholders or arguments so that `context` describes what failed, `m.Name` is the MCP name, and `m.Tenant` is the tenant—for example: `"failed to unmarshal MCP configuration '%s' (%s, tenant: '%s'): %w", m.Name, context, m.Tenant, err`.
</issue_to_address>

### Comment 2
<location path="pkg/mcp/server_types.go" line_range="398-408" />
<code_context>
+// @param text the text content
+// @param isError indicates if the result is an error
+// @return *CallToolResult the CallToolResult object with the text content and error flag
+func NewCallToolResultTextWithError(text string, isError bool) *CallToolResult {
+	return &CallToolResult{
+		Content: []Content{
+			&TextContent{
+				Type: TextContentType,
+				Text: text,
+			},
+		},
+		IsError: isError,
+	}
+}
</code_context>
<issue_to_address>
**suggestion:** NewCallToolResultTextWithError duplicates construction logic already present in NewCallToolResultText.

To avoid duplication and keep constructors aligned, you could delegate to `NewCallToolResultText` and then set `IsError`:

```go
func NewCallToolResultTextWithError(text string, isError bool) *CallToolResult {
    res := NewCallToolResultText(text)
    res.IsError = isError
    return res
}
```
This keeps text result construction centralized for future changes.

```suggestion
func NewCallToolResultTextWithError(text string, isError bool) *CallToolResult {
	res := NewCallToolResultText(text)
	res.IsError = isError
	return res
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@iFurySt
Copy link
Member

iFurySt commented Feb 28, 2026

LGTM. Thanks!

@iFurySt iFurySt merged commit f03b9e6 into AmoyLab:main Feb 28, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants