Skip to content

Commit d0f96ee

Browse files
javrudskyHarness
authored andcommitted
feat: [FME-4267]: Integrated Cost Categories Name listing (harness#16)
* Removing commented line * Removing unused import * Rebase with master branch * Adding prompt to reinforce parameters format for function Get Cost Overview from CCM * Fixing account id issue * Fixed account id field name * Added List Cloud Cost Management tool * [CCM-tools] CCM Overview * [CCM-tools] CCM Overview
1 parent ff2497d commit d0f96ee

File tree

6 files changed

+127
-7
lines changed

6 files changed

+127
-7
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ Toolset Name: `registries`
4949

5050
Toolset Name: `cloudcostmanagement`
5151

52-
- `get_ccm_overview`: Get the ccm overview of a specific account
52+
- `get_ccm_overview`: Retrieve the cost overview for a specific account.
53+
- `list_ccm_cost_categories`: List all cost categories for a specified account.
5354

5455
#### Logs Toolset
5556

client/cloudcostmanagement.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
const (
1010
ccmBasePath = "ccm/api"
1111
ccmGetOverviewPath = ccmBasePath + "/overview?accountIdentifier=%s&startTime=%d&endTime=%d&groupBy=%s"
12+
ccmCostCategoryListPath = ccmBasePath + "/business-mapping/filter-panel?accountIdentifier=%s"
1213
)
1314

1415
type CloudCostManagementService struct {
@@ -27,3 +28,31 @@ func (c *CloudCostManagementService) GetOverview(ctx context.Context, accID stri
2728

2829
return ccmOverview, nil
2930
}
31+
32+
func (r *CloudCostManagementService) ListCostCategories(ctx context.Context, scope dto.Scope, opts *dto.CcmListCostCategoriesOptions) (*dto.CCMCostCategoryList, error) {
33+
path := ccmCostCategoryListPath
34+
params := make(map[string]string)
35+
addScope(scope, params)
36+
37+
// Handle nil options by creating default options
38+
if opts == nil {
39+
opts = &dto.CcmListCostCategoriesOptions{}
40+
}
41+
42+
if opts.CostCategory != "" {
43+
params["costCategory"] = opts.CostCategory
44+
}
45+
if opts.SearchTerm != "" {
46+
params["search"] = opts.SearchTerm
47+
}
48+
49+
// Temporary slice to hold the strings
50+
costCategories := new(dto.CCMCostCategoryList)
51+
52+
err := r.Client.Get(ctx, path, params, nil, costCategories)
53+
if err != nil {
54+
return nil, fmt.Errorf("failed to list cloud cost managment cost categories: %w", err)
55+
}
56+
57+
return costCategories, nil
58+
}

client/dto/cloudcostmanagement.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,24 @@ const (
99
PeriodTypeYear string = "YEAR"
1010
)
1111

12-
// CEView represents a ccm overview
13-
type CEView struct {
12+
// CEView represents a basic ccm response.
13+
// The `data` field contains the response data.
14+
type CCMBaseResponse struct {
1415
Status string `json:"state,omitempty"`
15-
Data CCMOverview `json:"data,omitempty"`
16+
Message string `json:"message,omitempty"`
1617
CorrelationID string `json:"correlation_id,omitempty"`
18+
Error []CCMError `json:"error,omitempty"`
19+
}
20+
21+
// Response error
22+
type CCMError struct {
23+
FieldId string `json:"fieldId,omitempty"`
24+
Error string `json:"error,omitempty"`
25+
}
26+
27+
type CEView struct {
28+
CCMBaseResponse
29+
Data CCMOverview `json:"data,omitempty"`
1730
}
1831

1932
// CCMOverview represents the Overview data from a CCM Overview
@@ -42,3 +55,16 @@ type CCMReference struct {
4255
Name string `json:"name,omitempty"`
4356
Type string `json:"type,omitempty"`
4457
}
58+
59+
// CcmCostCategoriesOptions represents options for listing cost categories
60+
type CcmListCostCategoriesOptions struct {
61+
AccountIdentifier string `json:"accountIdentifier,omitempty"`
62+
CostCategory string `json:"costCategory,omitempty"`
63+
SearchTerm string `json:"search,omitempty"`
64+
}
65+
66+
// CcmCostCategoryList represents a list of cost categories in CCM
67+
type CCMCostCategoryList struct {
68+
CCMBaseResponse
69+
Data []string `json:"data,omitempty"`
70+
}

cmd/harness-mcp-server/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ func runStdioServer(ctx context.Context, config config.Config) error {
247247
}
248248

249249
slog.Info("Starting server", "url", config.BaseURL)
250+
slog.Debug("Using ", "Config ->", config)
250251

251252
// Define beforeInit function to add client info to user agent
252253
beforeInit := func(_ context.Context, _ any, message *mcp.InitializeRequest) {

pkg/harness/cloudcostmanagement.go

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,10 @@ func GetCcmOverviewTool(config *config.Config, client *client.CloudCostManagemen
5656
if err != nil {
5757
return mcp.NewToolResultError(err.Error()), nil
5858
}
59-
6059
startTime, err := utils.FormatMMDDYYYYToUnixMillis(startTimeStr)
6160
endTime, err := utils.FormatMMDDYYYYToUnixMillis(endTimeStr)
62-
6361
groupBy, err := requiredParam[string](request, "groupBy")
62+
6463
if err != nil {
6564
return mcp.NewToolResultError(err.Error()), nil
6665
}
@@ -79,6 +78,70 @@ func GetCcmOverviewTool(config *config.Config, client *client.CloudCostManagemen
7978
}
8079
}
8180

81+
82+
func ListCcmCostCategoriesTool(config *config.Config, client *client.CloudCostManagementService) (tool mcp.Tool, handler server.ToolHandlerFunc) {
83+
return mcp.NewTool("list_ccm_cost_categories",
84+
mcp.WithDescription("List the cost categories from an account in Harness Cloud Cost Management"),
85+
mcp.WithString("account_id",
86+
mcp.Description("The account identifier"),
87+
),
88+
mcp.WithString("cost_category",
89+
mcp.Description("Optional to search for specific category"),
90+
),
91+
mcp.WithString("search_term",
92+
mcp.Description("Optional search term to filter cost categories"),
93+
),
94+
WithScope(config, false),
95+
),
96+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
97+
accountId, err := OptionalParam[string](request, "account_id")
98+
if accountId == "" {
99+
accountId, err = getAccountID(config, request)
100+
}
101+
if err != nil {
102+
return mcp.NewToolResultError(err.Error()), nil
103+
}
104+
105+
params := &dto.CcmListCostCategoriesOptions{}
106+
params.AccountIdentifier = accountId
107+
108+
// Handle cost category parameter
109+
costCategory, ok, err := OptionalParamOK[string](request, "cost_category")
110+
if err != nil {
111+
return mcp.NewToolResultError(err.Error()), nil
112+
}
113+
if ok && costCategory != "" {
114+
params.CostCategory = costCategory
115+
}
116+
117+
// Handle search parameter
118+
searchTerm, ok, err := OptionalParamOK[string](request, "search_term")
119+
if err != nil {
120+
return mcp.NewToolResultError(err.Error()), nil
121+
}
122+
if ok && searchTerm != "" {
123+
params.SearchTerm = searchTerm
124+
}
125+
126+
scope, err := fetchScope(config, request, false)
127+
if err != nil {
128+
return mcp.NewToolResultError(err.Error()), nil
129+
}
130+
131+
data, err := client.ListCostCategories(ctx, scope, params)
132+
if err != nil {
133+
return nil, fmt.Errorf("failed to get CCM Cost Categories: %w", err)
134+
}
135+
136+
r, err := json.Marshal(data)
137+
if err != nil {
138+
return nil, fmt.Errorf("failed to marshal CCM Cost Category: %w", err)
139+
}
140+
141+
return mcp.NewToolResultText(string(r)), nil
142+
}
143+
}
144+
82145
// getAccountID retrieves AccountID from the config file
83146
func getAccountID(config *config.Config, request mcp.CallToolRequest) (string, error) {
84147
scope, scopeErr := fetchScope(config, request, true)

pkg/harness/tools.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ func InitToolsets(config *config.Config) (*toolsets.ToolsetGroup, error) {
5959
return nil, err
6060
}
6161

62-
6362
// Enable requested toolsets
6463
if err := tsg.EnableToolsets(config.Toolsets); err != nil {
6564
return nil, err
@@ -312,6 +311,7 @@ func registerCloudCostManagement(config *config.Config, tsg *toolsets.ToolsetGro
312311
ccm := toolsets.NewToolset("ccm", "Harness Cloud Cost Management related tools").
313312
AddReadTools(
314313
toolsets.NewServerTool(GetCcmOverviewTool(config, ccmClient)),
314+
toolsets.NewServerTool(ListCcmCostCategoriesTool(config, ccmClient)),
315315
)
316316

317317
// Add toolset to the group

0 commit comments

Comments
 (0)