Skip to content

Commit 4483b47

Browse files
Saranya-jenaHarness
authored andcommitted
chore: [ML-1319]: Added get_pipeline_summary and list_triggers tools and their e2e tests (#139)
* chore: [ML-1326]: updated branch Signed-off-by: Saranya-jena <[email protected]> * Merge branch 'master' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/mcp-server into ML-1319 * chore: [ML-1319]: resolved comments Signed-off-by: Saranya-jena <[email protected]> * chore: [ML-1319]: Added get_pipeline_summary and list_triggers tools and their e2e tests Signed-off-by: Saranya-jena <[email protected]>
1 parent 9def781 commit 4483b47

File tree

6 files changed

+584
-73
lines changed

6 files changed

+584
-73
lines changed

client/dto/pipeline.go

Lines changed: 105 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,14 @@ type ListOutput[T any] struct {
4040
// ListOutputData represents the data field of a list response
4141
type ListOutputData[T any] struct {
4242
TotalElements int `json:"totalElements,omitempty"`
43+
TotalItems int `json:"totalItems,omitempty"`
4344
TotalPages int `json:"totalPages,omitempty"`
4445
Size int `json:"size,omitempty"`
46+
PageSize int `json:"pageSize,omitempty"`
4547
Content []T `json:"content,omitempty"`
4648
Number int `json:"number,omitempty"`
49+
PageIndex int `json:"pageIndex,omitempty"`
50+
PageItemCount int `json:"pageItemCount,omitempty"`
4751
Sort SortInfo `json:"sort,omitempty"`
4852
First bool `json:"first,omitempty"`
4953
Pageable PageableInfo `json:"pageable,omitempty"`
@@ -63,6 +67,24 @@ type PipelineListOptions struct {
6367
SearchTerm string `json:"searchTerm,omitempty"`
6468
}
6569

70+
// PipelineSummary represents a summary of a pipeline
71+
type PipelineSummary struct {
72+
Identifier string `json:"identifier,omitempty"`
73+
Name string `json:"name,omitempty"`
74+
Description string `json:"description,omitempty"`
75+
Tags map[string]string `json:"tags,omitempty"`
76+
OrgIdentifier string `json:"orgIdentifier,omitempty"`
77+
ProjectIdentifier string `json:"projectIdentifier,omitempty"`
78+
Version int `json:"version,omitempty"`
79+
NumOfStages int `json:"numOfStages,omitempty"`
80+
CreatedAt int64 `json:"createdAt,omitempty"`
81+
LastUpdatedAt int64 `json:"lastUpdatedAt,omitempty"`
82+
Modules []string `json:"modules,omitempty"`
83+
ExecutionSummaryInfo *ExecutionSummaryInfo `json:"executionSummaryInfo,omitempty"`
84+
StageNames []string `json:"stageNames,omitempty"`
85+
YamlVersion string `json:"yamlVersion,omitempty"`
86+
}
87+
6688
// PipelineListItem represents an item in the pipeline list
6789
type PipelineListItem struct {
6890
Name string `json:"name,omitempty"`
@@ -234,20 +256,20 @@ type InputSetListData struct {
234256

235257
// InputSetDetail represents the detailed information of a specific input set
236258
type InputSetDetail struct {
237-
AccountId string `json:"accountId,omitempty"`
238-
OrgIdentifier string `json:"orgIdentifier,omitempty"`
239-
ProjectIdentifier string `json:"projectIdentifier,omitempty"`
240-
PipelineIdentifier string `json:"pipelineIdentifier,omitempty"`
241-
Identifier string `json:"identifier,omitempty"`
242-
InputSetYaml string `json:"inputSetYaml,omitempty"`
243-
Name string `json:"name,omitempty"`
244-
Description string `json:"description,omitempty"`
245-
Tags map[string]string `json:"tags,omitempty"`
246-
InputSetErrorWrapper InputSetErrorWrapper `json:"inputSetErrorWrapper,omitempty"`
247-
GitDetails GitDetails `json:"gitDetails,omitempty"`
248-
EntityValidityDetails EntityValidityDetails `json:"entityValidityDetails,omitempty"`
249-
Outdated bool `json:"outdated,omitempty"`
250-
ErrorResponse bool `json:"errorResponse,omitempty"`
259+
AccountId string `json:"accountId,omitempty"`
260+
OrgIdentifier string `json:"orgIdentifier,omitempty"`
261+
ProjectIdentifier string `json:"projectIdentifier,omitempty"`
262+
PipelineIdentifier string `json:"pipelineIdentifier,omitempty"`
263+
Identifier string `json:"identifier,omitempty"`
264+
InputSetYaml string `json:"inputSetYaml,omitempty"`
265+
Name string `json:"name,omitempty"`
266+
Description string `json:"description,omitempty"`
267+
Tags map[string]string `json:"tags,omitempty"`
268+
InputSetErrorWrapper InputSetErrorWrapper `json:"inputSetErrorWrapper,omitempty"`
269+
GitDetails GitDetails `json:"gitDetails,omitempty"`
270+
EntityValidityDetails EntityValidityDetails `json:"entityValidityDetails,omitempty"`
271+
Outdated bool `json:"outdated,omitempty"`
272+
ErrorResponse bool `json:"errorResponse,omitempty"`
251273
}
252274

253275
// InputSetErrorWrapper represents the error wrapper for input sets
@@ -266,3 +288,72 @@ type InputSetResponse struct {
266288
CorrelationId string `json:"correlationId,omitempty"`
267289
}
268290

291+
// TriggerListOptions represents the options for listing triggers
292+
type TriggerListOptions struct {
293+
PaginationOptions
294+
TriggerNames []string `json:"triggerNames,omitempty"`
295+
TriggerIdentifiers []string `json:"triggerIdentifiers,omitempty"`
296+
TriggerTypes []string `json:"triggerTypes,omitempty"`
297+
Tags map[string]string `json:"tags,omitempty"`
298+
SearchTerm string `json:"searchTerm,omitempty"`
299+
TargetIdentifier string `json:"targetIdentifier,omitempty"`
300+
Filter string `json:"filter,omitempty"`
301+
}
302+
303+
// WebhookDetails contains webhook configuration details
304+
type WebhookDetails struct {
305+
WebhookSecret string `json:"webhookSecret,omitempty"`
306+
WebhookSourceRepo string `json:"webhookSourceRepo,omitempty"`
307+
}
308+
309+
// ValidationStatus represents the validation status of a trigger
310+
type ValidationStatus struct {
311+
StatusResult string `json:"statusResult,omitempty"`
312+
DetailedMessage string `json:"detailedMessage,omitempty"`
313+
}
314+
315+
// WebhookAutoRegistrationStatus represents the webhook registration status
316+
type WebhookAutoRegistrationStatus struct {
317+
RegistrationResult string `json:"registrationResult,omitempty"`
318+
DetailedMessage string `json:"detailedMessage,omitempty"`
319+
}
320+
321+
// WebhookInfo contains webhook identification information
322+
type WebhookInfo struct {
323+
WebhookId string `json:"webhookId,omitempty"`
324+
}
325+
326+
// TriggerStatus contains the status information for a trigger
327+
type TriggerStatus struct {
328+
PollingSubscriptionStatus interface{} `json:"pollingSubscriptionStatus,omitempty"`
329+
ValidationStatus *ValidationStatus `json:"validationStatus,omitempty"`
330+
WebhookAutoRegistrationStatus *WebhookAutoRegistrationStatus `json:"webhookAutoRegistrationStatus,omitempty"`
331+
WebhookInfo *WebhookInfo `json:"webhookInfo,omitempty"`
332+
Status string `json:"status,omitempty"`
333+
DetailMessages []string `json:"detailMessages,omitempty"`
334+
LastPollingUpdate interface{} `json:"lastPollingUpdate,omitempty"`
335+
LastPolled interface{} `json:"lastPolled,omitempty"`
336+
}
337+
338+
type TriggerListItem struct {
339+
Name string `json:"name,omitempty"`
340+
Identifier string `json:"identifier,omitempty"`
341+
Description string `json:"description,omitempty"`
342+
Tags map[string]string `json:"tags,omitempty"`
343+
Type string `json:"type,omitempty"`
344+
Enabled bool `json:"enabled,omitempty"`
345+
Yaml string `json:"yaml,omitempty"`
346+
WebhookUrl string `json:"webhookUrl,omitempty"`
347+
RegistrationStatus string `json:"registrationStatus,omitempty"`
348+
YamlVersion string `json:"yamlVersion,omitempty"`
349+
TriggerStatus *TriggerStatus `json:"triggerStatus,omitempty"`
350+
WebhookDetails *WebhookDetails `json:"webhookDetails,omitempty"`
351+
Executions []int `json:"executions,omitempty"`
352+
WebhookCurlCommand string `json:"webhookCurlCommand,omitempty"`
353+
PipelineInputOutdated bool `json:"pipelineInputOutdated,omitempty"`
354+
// Keeping these fields for backward compatibility
355+
CreatedAt int64 `json:"createdAt,omitempty"`
356+
LastUpdatedAt int64 `json:"lastUpdatedAt,omitempty"`
357+
TargetIdentifier string `json:"targetIdentifier,omitempty"`
358+
PipelineIdentifier string `json:"pipelineIdentifier,omitempty"`
359+
}

client/pipelines.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package client
33
import (
44
"context"
55
"fmt"
6+
"strconv"
67

78
"github.com/harness/harness-mcp/client/dto"
89
)
@@ -15,6 +16,8 @@ const (
1516
pipelineExecutionSummaryPath = "/api/pipelines/execution/summary"
1617
pipelineInputSetListPath = "/api/inputSets"
1718
pipelineInputSetPath = "/api/inputSets/%s"
19+
pipelineTriggersPath = "/api/triggers"
20+
pipelineSummaryPath = "/api/pipelines/summary/%s"
1821
)
1922

2023
type PipelineService struct {
@@ -266,3 +269,85 @@ func (p *PipelineService) GetInputSet(
266269

267270
return response, nil
268271
}
272+
273+
func (p *PipelineService) ListTriggers(
274+
ctx context.Context,
275+
scope dto.Scope,
276+
opts *dto.TriggerListOptions,
277+
) (*dto.ListOutput[dto.TriggerListItem], error) {
278+
path := pipelineTriggersPath
279+
280+
// Prepare query parameters
281+
params := make(map[string]string)
282+
addScope(scope, params)
283+
284+
// Handle nil options by creating default options
285+
if opts == nil {
286+
opts = &dto.TriggerListOptions{}
287+
}
288+
289+
// Set default pagination
290+
setDefaultPagination(&opts.PaginationOptions)
291+
292+
// Add pagination parameters
293+
params["page"] = fmt.Sprintf("%d", opts.Page)
294+
params["size"] = fmt.Sprintf("%d", opts.Size)
295+
296+
// Add optional parameters if provided
297+
if opts.SearchTerm != "" {
298+
params["searchTerm"] = opts.SearchTerm
299+
}
300+
if opts.TargetIdentifier != "" {
301+
params["targetIdentifier"] = opts.TargetIdentifier
302+
}
303+
if opts.Filter != "" {
304+
params["filter"] = opts.Filter
305+
}
306+
307+
// Add required parameters for the gateway endpoint based on the curl command
308+
params["accountIdentifier"] = scope.AccountID
309+
params["orgIdentifier"] = scope.OrgID
310+
params["projectIdentifier"] = scope.ProjectID
311+
312+
// Initialize the response object
313+
response := &dto.ListOutput[dto.TriggerListItem]{}
314+
315+
// Make the GET request (the API uses GET for this list operation)
316+
err := p.Client.Get(ctx, path, params, map[string]string{}, response)
317+
if err != nil {
318+
return nil, fmt.Errorf("failed to list triggers: %w", err)
319+
}
320+
321+
return response, nil
322+
}
323+
324+
// GetPipelineSummary gets a summary of a pipeline
325+
func (p *PipelineService) GetPipelineSummary(
326+
ctx context.Context,
327+
scope dto.Scope,
328+
pipelineIdentifier string,
329+
getMetadataOnly bool,
330+
) (*dto.Entity[dto.PipelineSummary], error) {
331+
// Format the pipeline path first to replace its %s placeholder
332+
path := fmt.Sprintf(pipelineSummaryPath, pipelineIdentifier)
333+
334+
// Prepare query parameters
335+
params := make(map[string]string)
336+
addScope(scope, params)
337+
338+
// Add the getMetadataOnly parameter
339+
params["getMetadataOnly"] = strconv.FormatBool(getMetadataOnly)
340+
341+
// Add required parameters for the endpoint
342+
params["accountIdentifier"] = scope.AccountID
343+
params["orgIdentifier"] = scope.OrgID
344+
params["projectIdentifier"] = scope.ProjectID
345+
346+
var responseData dto.Entity[dto.PipelineSummary]
347+
err := p.Client.Get(ctx, path, params, map[string]string{}, &responseData)
348+
if err != nil {
349+
return nil, fmt.Errorf("failed to get pipeline summary: %w", err)
350+
}
351+
352+
return &responseData, nil
353+
}

pkg/harness/prompts/prompts.go

Lines changed: 3 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,69 +14,22 @@ func RegisterPrompts(mcpServer *server.MCPServer) {
1414
p.NewPrompt().SetName("get_ccm_overview").
1515
SetDescription("Ensure parameters are provided correctly and in the right format. ").
1616
SetResultDescription("Input parameters validation").
17-
SetText(`When calling get_ccm_overview, ensure you have: accountIdentifier, groupBy, startDate, and endDate.
18-
- If any are missing, ask the user for the specific value(s).
19-
- Always send startDate and endDate in the following format: 'MM/DD/YYYY' (e.g. '10/30/2025')
20-
- If no dates are supplied, default startDate to 60 days ago and endDate to now.`).
17+
SetText(`{"standard": "When calling get_ccm_overview, ensure you have: accountIdentifier, groupBy, startDate, and endDate.\n\t\t\t\t\t- If any are missing, ask the user for the specific value(s).\n\t\t\t\t\t- Always send startDate and endDate in the following format: 'MM/DD/YYYY' (e.g. '10/30/2025')\n\t\t\t\t\t- If no dates are supplied, default startDate to 60 days ago and endDate to now."}`).
2118
Build())
2219

2320
prompts.Append(
2421
p.NewPrompt().SetName("ask_confirmation_for_update_and_delete_operations").
2522
SetDescription("Ensure that Update or Delete operations are executed ONLY after user confirmation.").
2623
SetResultDescription("Execute operation if user input 'yes', cancel otherwise.").
27-
SetText(`
28-
**Confirmation Policy**:
29-
When a function/tool description contains the tag <INSERT_TOOL>, <UPDATE_TOOL> or <DELETE_TOOL>, **BEFORE** calling it you **ALWAYS** must:
30-
31-
- Present a clear, minimal summary of the impending change (show key fields/values).
32-
- Ask: 'Please confirm to proceed (yes/no).'
33-
- **ONLY** invoke the tool if the user’s next message is exactly “yes” (case-insensitive).
34-
- If the user’s answer is anything other than “yes”, do not call the tool; instead, offer to adjust or cancel.
35-
- Never assume consent; always re-ask if the context is ambiguous or stale.
36-
`).
24+
SetText(`{"standard": "**Confirmation Policy**:\nWhen a function/tool description contains the tag <INSERT_TOOL>, <UPDATE_TOOL> or <DELETE_TOOL>, **BEFORE** calling it you **ALWAYS** must:\n\n- Present a clear, minimal summary of the impending change (show key fields/values).\n- Ask: 'Please confirm to proceed (yes/no).'\n- **ONLY** invoke the tool if the user's next message is exactly \"yes\" (case-insensitive).\n- If the user's answer is anything other than \"yes\", do not call the tool; instead, offer to adjust or cancel.\n- Never assume consent; always re-ask if the context is ambiguous or stale."}`).
3725
Build())
3826

3927
// Pipeline summarization prompt
4028
prompts.Append(
4129
p.NewPrompt().SetName("pipeline_summarizer").
4230
SetDescription("Summarize a Harness pipeline's structure, purpose, and behavior.").
4331
SetResultDescription("Comprehensive pipeline summary with key details.").
44-
SetText(`I need you to summarise the pipeline with the input pipeline identifier.
45-
46-
1. **What to do?**
47-
- Fetch any required metadata or definitions for the pipeline.
48-
- Analyze its configuration and structure.
49-
- Make the necessary tool calls to get the pipeline related details.
50-
- Produce a concise, accurate summary of the pipeline's design and behavior.
51-
52-
2. **What tools to call?**
53-
- get_pipeline
54-
- list_pipelines
55-
- get_environment
56-
- get_service
57-
58-
3. **Must-have details in the output** (acceptance criteria):
59-
- **Purpose and Objective**: What this pipeline is designed to accomplish (e.g. "Builds and deploys a Node.js microservice to staging and production.")
60-
- **High-Level Architecture**: Major components and phases (build, test, security scanning, deployment).
61-
- **Environment Flow**: How the execution moves through environments.
62-
- **Key Technologies**: Languages, frameworks, deployment targets, and tools used.
63-
- **Trigger Conditions**: What events start the pipeline (Git commits, manual triggers, schedules).
64-
- **Approval Gates**: Any manual approvals required, and who must sign off.
65-
- **Dependencies**: External dependencies such as environments, infrastructures, connectors, services, other pipelines this one relies on, etc with their ids if available.
66-
- **Success Criteria**: What defines a successful run.
67-
68-
4. **Output format**
69-
Return the following data ONLY in a markdown format, DO NOT use JSON literals:
70-
{
71-
"purpose": string,
72-
"architecture": string,
73-
"environment": string,
74-
"technologies": string[],
75-
"triggers": string[],
76-
"approvals": string[],
77-
"dependencies": string[],
78-
"success_criteria": string
79-
}`).
32+
SetText(`{"standard": "I need you to summarise the pipeline with the input pipeline identifier.\n\n1. **What to do?**\n - Fetch any required metadata or definitions for the pipeline.\n - Analyze its configuration and structure.\n - Make the necessary tool calls to get the pipeline related details.\n - Produce a concise, accurate summary of the pipeline's design and behavior.\n\n2. **Tools to call to get a complete overview of pipeline**\n - get_pipeline\n - get_pipeline_summary\n - list_pipelines\n - get_environment\n - get_service\n - list_settings (with category as NOTIFICATIONS)\n - get_secret\n - list_triggers\n\n3. **Must-have details in the output** (acceptance criteria):\n - **Purpose and Objective**: What this pipeline is designed to accomplish (e.g. \"Builds and deploys a Node.js microservice to staging and production.\")\n - **High-Level Architecture**: Major components and phases (build, test, security scanning, deployment).\n - **Environment Flow**: How the execution moves through environments.\n - **Key Technologies**: Languages, frameworks, deployment targets, and tools used.\n - **Trigger Conditions**: What events start the pipeline (Git commits, manual triggers, schedules).\n - **Approval Gates**: Any manual approvals required, and who must sign off.\n - **Dependencies**: External dependencies such as environments, infrastructures, connectors, services, other pipelines this one relies on, etc with their ids if available.\n - **Success Criteria**: What defines a successful run.\n\n4. **Output format**\n Return the following data ONLY in a markdown format, DO NOT use JSON literals:\n {\n \"purpose\": string,\n \"architecture\": string,\n \"environment\": string,\n \"technologies\": string[],\n \"triggers\": string[],\n \"approvals\": string[],\n \"dependencies\": string[],\n \"success_criteria\": string,\n \"past_execution_details\": string[]\n }"}`).
8033
Build())
8134

8235
p.AddPrompts(prompts, mcpServer)

0 commit comments

Comments
 (0)