Skip to content

Commit aadbf09

Browse files
yogesh-chauhanHarness
authored andcommitted
feat: [ML-1118]: add service, environment and infra capabilities to mcp-server (harness#14)
* fix rest of clients * Fix accountId formatting and other params for listing tools * refactor: remove policy and gitops functionality from mcp-server * connector * chore: remove testify and related dependencies from go.mod * feat: add GitOps data types and refactor array parameter handling * feat: implement GitOps, infrastructure, policy, services and environments APIs
1 parent d0f96ee commit aadbf09

16 files changed

+1638
-9
lines changed

client/client.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,22 @@ func addQueryParams(req *http.Request, params map[string]string) {
317317
}
318318

319319
func addScope(scope dto.Scope, params map[string]string) {
320-
params["accountIdentifier"] = scope.AccountID
321-
params["orgIdentifier"] = scope.OrgID
322-
params["projectIdentifier"] = scope.ProjectID
320+
if scope.AccountID == "" {
321+
slog.Error("Account ID is empty in scope")
322+
} else {
323+
params["accountIdentifier"] = scope.AccountID
324+
params["accountId"] = scope.AccountID
325+
}
326+
327+
if scope.OrgID != "" {
328+
params["orgIdentifier"] = scope.OrgID
329+
params["orgId"] = scope.OrgID
330+
}
331+
332+
if scope.ProjectID != "" {
333+
params["projectIdentifier"] = scope.ProjectID
334+
params["projectId"] = scope.ProjectID
335+
}
323336
}
324337

325338
func setDefaultPagination(opts *dto.PaginationOptions) {

client/connectors.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,59 @@
11
package client
22

3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/harness/harness-mcp/client/dto" // Corrected import path for Scope
8+
pkgDTO "github.com/harness/harness-mcp/pkg/harness/dto" // Alias for the other DTOs
9+
)
10+
311
type ConnectorService struct {
412
Client *Client
513
}
14+
15+
// ListConnectorCatalogue fetches the connector catalogue.
16+
// API Documentation: https://apidocs.harness.io/tag/Connectors#operation/getConnectorCatalogue
17+
func (c *ConnectorService) ListConnectorCatalogue(ctx context.Context, scope dto.Scope) ([]pkgDTO.ConnectorCatalogueItem, error) {
18+
path := "/ng/api/connectors/catalogue"
19+
params := make(map[string]string)
20+
params["accountIdentifier"] = scope.AccountID
21+
if scope.OrgID != "" {
22+
params["orgIdentifier"] = scope.OrgID
23+
}
24+
if scope.ProjectID != "" {
25+
params["projectIdentifier"] = scope.ProjectID
26+
}
27+
28+
// Define a struct to match the actual API response structure
29+
type catalogueResponse struct {
30+
Status string `json:"status"`
31+
Data struct {
32+
Catalogue []struct {
33+
Category string `json:"category"`
34+
Connectors []string `json:"connectors"`
35+
} `json:"catalogue"`
36+
} `json:"data"`
37+
MetaData interface{} `json:"metaData"`
38+
CorrelationID string `json:"correlationId"`
39+
}
40+
41+
var rawResponse catalogueResponse
42+
err := c.Client.Get(ctx, path, params, nil, &rawResponse)
43+
if err != nil {
44+
return nil, fmt.Errorf("failed to list connector catalogue: %w", err)
45+
}
46+
47+
// Convert the response to the expected format
48+
var result []pkgDTO.ConnectorCatalogueItem
49+
for _, cat := range rawResponse.Data.Catalogue {
50+
for _, conn := range cat.Connectors {
51+
result = append(result, pkgDTO.ConnectorCatalogueItem{
52+
Category: cat.Category,
53+
Name: conn,
54+
})
55+
}
56+
}
57+
58+
return result, nil
59+
}

client/dto/environment.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package dto
2+
3+
// Environment represents a Harness environment
4+
type Environment struct {
5+
ID string `json:"identifier"`
6+
Name string `json:"name"`
7+
Description string `json:"description,omitempty"`
8+
OrgIdentifier string `json:"orgIdentifier"`
9+
ProjectIdentifier string `json:"projectIdentifier"`
10+
Color string `json:"color,omitempty"`
11+
Type string `json:"type,omitempty"` // e.g., "PreProduction", "Production"
12+
Tags map[string]string `json:"tags,omitempty"`
13+
YAML string `json:"yaml,omitempty"`
14+
Variables []map[string]interface{} `json:"variables,omitempty"`
15+
GitOpsEnabled bool `json:"gitOpsEnabled,omitempty"`
16+
CreatedAt int64 `json:"createdAt,omitempty"`
17+
LastModifiedAt int64 `json:"lastModifiedAt,omitempty"`
18+
}
19+
20+
// EnvironmentResponse represents the response from the get environment API
21+
type EnvironmentResponse struct {
22+
Data Environment `json:"data"`
23+
}
24+
25+
// EnvironmentListResponse represents the response from the list environments API
26+
type EnvironmentListResponse struct {
27+
Data struct {
28+
Content []Environment `json:"content"`
29+
TotalPages int `json:"totalPages"`
30+
TotalElements int `json:"totalElements"`
31+
PageSize int `json:"pageSize"`
32+
PageIndex int `json:"pageIndex"`
33+
} `json:"data"`
34+
}
35+
36+
// EnvironmentOptions represents the options for listing environments
37+
type EnvironmentOptions struct {
38+
Page int `json:"page,omitempty"`
39+
Limit int `json:"limit,omitempty"`
40+
Sort string `json:"sort,omitempty"`
41+
Order string `json:"order,omitempty"`
42+
}
43+
44+
type MoveConfigType string
45+
46+
const (
47+
InlineToRemote MoveConfigType = "INLINE_TO_REMOTE"
48+
RemoteToInline MoveConfigType = "REMOTE_TO_INLINE"
49+
)
50+
51+
type MoveEnvironmentConfigsRequest struct {
52+
EnvironmentIdentifier string `json:"-"` // Not part of the body but needed for the path
53+
54+
AccountIdentifier string `json:"-"` // Required
55+
OrgIdentifier string `json:"-"`
56+
ProjectIdentifier string `json:"-"`
57+
ConnectorRef string `json:"-"`
58+
RepoName string `json:"-"`
59+
Branch string `json:"-"`
60+
FilePath string `json:"-"`
61+
CommitMsg string `json:"-"`
62+
IsNewBranch *bool `json:"-"`
63+
BaseBranch string `json:"-"`
64+
IsHarnessCodeRepo *bool `json:"-"`
65+
MoveConfigType MoveConfigType `json:"-"` // Required
66+
}
67+
type HarnessErrorMessage struct {
68+
Code string `json:"code"`
69+
Level string `json:"level"`
70+
Message string `json:"message"`
71+
Exception interface{} `json:"exception"`
72+
FailureTypes []string `json:"failureTypes"`
73+
AdditionalInfo map[string]interface{} `json:"additionalInfo"`
74+
}
75+
76+
type MoveEnvironmentConfigsResponse struct {
77+
Status string `json:"status"`
78+
CorrelationId string `json:"correlationId"`
79+
MetaData any `json:"metaData"`
80+
81+
Data struct {
82+
Identifier string `json:"identifier"`
83+
Success bool `json:"success"`
84+
} `json:"data,omitempty"`
85+
86+
Code string `json:"code,omitempty"`
87+
Message string `json:"message,omitempty"`
88+
DetailedMessage string `json:"detailedMessage,omitempty"`
89+
ResponseMessages []HarnessErrorMessage `json:"responseMessages,omitempty"`
90+
}

client/dto/infrastructure.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package dto
2+
3+
// Infrastructure represents a Harness infrastructure definition
4+
type Infrastructure struct {
5+
ID string `json:"identifier"`
6+
Name string `json:"name"`
7+
Description string `json:"description,omitempty"`
8+
OrgIdentifier string `json:"orgIdentifier"`
9+
ProjectIdentifier string `json:"projectIdentifier"`
10+
EnvironmentRef string `json:"environmentRef"`
11+
Type string `json:"type,omitempty"`
12+
DeploymentType string `json:"deploymentType,omitempty"` // e.g., Kubernetes, ECS, etc.
13+
YAML string `json:"yaml,omitempty"`
14+
Tags map[string]string `json:"tags,omitempty"`
15+
Variables []map[string]interface{} `json:"variables,omitempty"`
16+
GitOpsEnabled bool `json:"gitOpsEnabled,omitempty"`
17+
AccountID string `json:"accountId,omitempty"`
18+
StoreType string `json:"storeType,omitempty"`
19+
}
20+
21+
// InfrastructureItem represents an item in the response list
22+
type InfrastructureItem struct {
23+
Infrastructure Infrastructure `json:"infrastructure"`
24+
CreatedAt int64 `json:"createdAt,omitempty"`
25+
LastModifiedAt int64 `json:"lastModifiedAt,omitempty"`
26+
EntityValidityDetails interface{} `json:"entityValidityDetails"`
27+
GovernanceMetadata interface{} `json:"governanceMetadata"`
28+
}
29+
30+
// InfrastructureListResponse represents the response from the list infrastructures API
31+
type InfrastructureListResponse struct {
32+
Status string `json:"status,omitempty"`
33+
MetaData interface{} `json:"metaData"`
34+
CorrelationID string `json:"correlationId,omitempty"`
35+
Data struct {
36+
Content []InfrastructureItem `json:"content"`
37+
TotalPages int `json:"totalPages"`
38+
TotalItems int `json:"totalItems"`
39+
PageItemCount int `json:"pageItemCount"`
40+
PageSize int `json:"pageSize"`
41+
PageIndex int `json:"pageIndex"`
42+
Empty bool `json:"empty"`
43+
PageToken interface{} `json:"pageToken"`
44+
} `json:"data"`
45+
}
46+
47+
// InfrastructureOptions represents the options for listing infrastructures
48+
type InfrastructureOptions struct {
49+
Page int `json:"page,omitempty"`
50+
Limit int `json:"limit,omitempty"`
51+
Sort string `json:"sort,omitempty"`
52+
Order string `json:"order,omitempty"`
53+
DeploymentType string `json:"deploymentType,omitempty"` // Filter by deployment type
54+
EnvironmentIdentifier string `json:"environmentIdentifier,omitempty"` // Filter by environment
55+
}
56+
57+
// MoveInfraConfigsRequest represents the request to move infrastructure configurations
58+
type MoveInfraConfigsRequest struct {
59+
InfraIdentifier string `json:"-"` // Required - from path parameter
60+
EnvironmentIdentifier string `json:"-"` // Required
61+
AccountIdentifier string `json:"-"` // Required
62+
OrgIdentifier string `json:"-"`
63+
ProjectIdentifier string `json:"-"`
64+
ConnectorRef string `json:"-"`
65+
RepoName string `json:"-"`
66+
Branch string `json:"-"`
67+
FilePath string `json:"-"`
68+
CommitMsg string `json:"-"`
69+
IsNewBranch *bool `json:"-"`
70+
BaseBranch string `json:"-"`
71+
IsHarnessCodeRepo *bool `json:"-"`
72+
MoveConfigType MoveConfigType `json:"-"` // Required - enum: "INLINE_TO_REMOTE" "REMOTE_TO_INLINE"
73+
}
74+
75+
// MoveInfraConfigsResponse represents the response from the move infrastructure configs API
76+
type MoveInfraConfigsResponse struct {
77+
Status string `json:"status"`
78+
CorrelationId string `json:"correlationId"`
79+
MetaData any `json:"metaData"`
80+
81+
Data struct {
82+
Identifier string `json:"identifier"`
83+
Success bool `json:"success"`
84+
} `json:"data,omitempty"`
85+
86+
Code string `json:"code,omitempty"`
87+
Message string `json:"message,omitempty"`
88+
DetailedMessage string `json:"detailedMessage,omitempty"`
89+
ResponseMessages []HarnessErrorMessage `json:"responseMessages,omitempty"`
90+
}

client/dto/service.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package dto
2+
3+
// Service represents a service in Harness
4+
type Service struct {
5+
ID string `json:"identifier"`
6+
Name string `json:"name"`
7+
Description string `json:"description,omitempty"`
8+
OrgIdentifier string `json:"orgIdentifier"`
9+
ProjectIdentifier string `json:"projectIdentifier"`
10+
YAML string `json:"yaml,omitempty"`
11+
Tags map[string]string `json:"tags,omitempty"`
12+
Variables []map[string]interface{} `json:"variables,omitempty"`
13+
GitOpsEnabled bool `json:"gitOpsEnabled,omitempty"`
14+
CreatedAt int64 `json:"createdAt,omitempty"`
15+
LastModifiedAt int64 `json:"lastModifiedAt,omitempty"`
16+
}
17+
18+
// ServiceResponse represents the response from the get service API
19+
type ServiceResponse struct {
20+
Data Service `json:"data"`
21+
}
22+
23+
// ServiceListResponse represents the response from the list services API
24+
type ServiceListResponse struct {
25+
Data struct {
26+
Content []Service `json:"content"`
27+
TotalPages int `json:"totalPages"`
28+
TotalElements int `json:"totalElements"`
29+
PageSize int `json:"pageSize"`
30+
PageIndex int `json:"pageIndex"`
31+
} `json:"data"`
32+
}
33+
34+
// ServiceOptions represents the options for listing services
35+
type ServiceOptions struct {
36+
Page int `json:"page,omitempty"`
37+
Limit int `json:"limit,omitempty"`
38+
Sort string `json:"sort,omitempty"`
39+
Order string `json:"order,omitempty"`
40+
// Additional filtering options can be added as needed
41+
}

0 commit comments

Comments
 (0)