package mcp import ( "context" "strings" ) // mcpTool adapts an MCP tool to the pkg/plugins/sdk Tool interface so it can be // registered and invoked like any built-in tool. It is immutable and safe for // concurrent use. type mcpTool struct { client *Client info ToolInfo } // Name returns the MCP tool identifier. func (t *mcpTool) Name() string { return t.info.Name } // Description returns the MCP tool description. func (t *mcpTool) Description() string { return t.info.Description } // Parameters returns the MCP tool input schema, defaulting to an empty object // schema when the server omits one. func (t *mcpTool) Parameters() (map[string]any, error) { if t.info.InputSchema == nil { return map[string]any{"type": "object", "properties": map[string]any{}}, nil } return t.info.InputSchema, nil } // Execute invokes the MCP tool and returns its concatenated text content. func (t *mcpTool) Execute(ctx context.Context, args any) (any, error) { res, err := t.client.CallTool(ctx, t.info.Name, args) if err != nil { return nil, err } return contentText(res.Content), nil } // contentText concatenates the text blocks of a tool result into a single // string, ignoring non-text blocks. func contentText(content []ContentBlock) string { var b strings.Builder for _, blk := range content { if blk.Type == "" || blk.Type == "text" { b.WriteString(blk.Text) } } return b.String() } // toContentBlocks converts wire content blocks to the public content block type. func toContentBlocks(wire []contentBlock) []ContentBlock { out := make([]ContentBlock, 0, len(wire)) for _, c := range wire { out = append(out, ContentBlock{Type: c.Type, Text: c.Text}) } return out }