Harness MCP Skill
AI-powered CD operations, Git repository and pull request management via Harness MCP Server.
Prerequisites
Environment Variables
export HARNESS_API_KEY="your-api-key" export HARNESS_DEFAULT_ORG_ID="your-org-id" export HARNESS_DEFAULT_PROJECT_ID="your-project-id" export HARNESS_BASE_URL="https://app.harness.io" export HARNESS_ACCOUNT_ID="your-account-id"
API Token Generation
-
Navigate to Account Settings > API Keys in Harness UI
-
Click + API Key
-
Set permissions (minimum: pipeline execution, connector management)
-
Store securely
MCP Server Configuration
Claude Code
{ "mcpServers": { "harness": { "command": "npx", "args": ["-y", "@anthropic-ai/mcp-harness"], "env": { "HARNESS_API_KEY": "${HARNESS_API_KEY}", "HARNESS_DEFAULT_ORG_ID": "${HARNESS_DEFAULT_ORG_ID}", "HARNESS_DEFAULT_PROJECT_ID": "${HARNESS_DEFAULT_PROJECT_ID}", "HARNESS_BASE_URL": "${HARNESS_BASE_URL}" } } } }
Docker
docker run -e HARNESS_API_KEY=$HARNESS_API_KEY
-e HARNESS_DEFAULT_ORG_ID=$HARNESS_DEFAULT_ORG_ID
-e HARNESS_DEFAULT_PROJECT_ID=$HARNESS_DEFAULT_PROJECT_ID
harness/mcp-server:latest
Available MCP Tools
Category Tool Purpose
Connectors harness_get_connector , harness_list_connectors , harness_get_connector_catalogue
Manage connectors
Pipelines harness_list_pipelines , harness_get_pipeline , harness_trigger_pipeline
Pipeline operations
Executions harness_get_execution , harness_list_executions , harness_get_execution_url
Track executions
Dashboards harness_list_dashboards , harness_get_dashboard
Dashboard data
Repos harness_get_repository , harness_list_repositories
Repository management
Pull Requests harness_get_pull_request , harness_list_pull_requests , harness_create_pull_request , harness_get_pull_request_checks , harness_get_pull_request_activities
PR operations
Git & Pull Request Workflows
List Repositories
repos = harness_list_repositories( org_id="${HARNESS_ORG_ID}", project_id="${HARNESS_PROJECT_ID}" )
Create Pull Request
pr = harness_create_pull_request( repo_id="my-application", title="PROJ-123: Feature title", source_branch="feature/PROJ-123", target_branch="main", description="## Summary\nImplements feature.\n## Jira\nPROJ-123" )
Get PR Activities (Comments, Reviews)
activities = harness_get_pull_request_activities(repo_id="my-app", pr_number=42) for activity in activities: if activity.type == "comment": print(f"Comment by {activity.author} at {activity.file_path}:{activity.line_number}") elif activity.type == "review": print(f"Review by {activity.author}: {activity.state}")
Sync PR Comments to Jira
activities = harness_get_pull_request_activities(repo_id="my-app", pr_number=42) review_summary = [] for activity in activities: if activity.type == "review": review_summary.append(f"- {activity.author}: {activity.state}")
jira_add_comment(issue_key="PROJ-123", body=f"## PR Review\nPR: #{42}\nStatus: {pr.state}\n\n" + "\n".join(review_summary))
PR-to-Jira Status Mapping
pr_sync: enabled: true jira_key_patterns: - title: "^([A-Z]+-\d+)" - branch: "feature/([A-Z]+-\d+)" transitions: pr_created: { transition: "In Review", comment: "PR created: {pr_url}" } pr_approved: { transition: "Approved", comment: "PR approved by {approver}" } pr_merged: { transition: "Done", comment: "PR merged to {target_branch}" } fields: pr_url: "customfield_10200" pr_status: "customfield_10201" reviewers: "customfield_10202"
Jira Connector Setup
Create Connector
connector: name: jira-connector identifier: jira_connector type: Jira spec: jiraUrl: https://your-company.atlassian.net auth: type: UsernamePassword spec: username: your.email@company.com passwordRef: jira_api_token delegateSelectors: - delegate-name
Required Scopes: read:jira-user , read:jira-work , write:jira-work
Jira Create Step in Pipeline
- step: name: Create Jira Issue type: JiraCreate spec: connectorRef: jira_connector projectKey: PROJ issueType: Task fields: - name: Summary value: "Deployment: <+pipeline.name> - <+pipeline.sequenceId>" - name: Priority value: Medium
Jira Update Step
- step: name: Update Jira Issue type: JiraUpdate spec: connectorRef: jira_connector issueKey: <+pipeline.variables.jiraIssueKey> fields: - name: Status value: Done transitionTo: transitionName: Done status: Done
Jira Approval Step
- step: name: Jira Approval type: JiraApproval spec: connectorRef: jira_connector issueKey: <+pipeline.variables.jiraIssueKey> approvalCriteria: matchAnyCondition: true conditions: - key: Status operator: equals value: Approved
Integration with Jira Orchestrator
Configuration
harness: account: account_id: "${HARNESS_ACCOUNT_ID}" org_id: "${HARNESS_ORG_ID}" project_id: "${HARNESS_PROJECT_ID}" api: base_url: "https://app.harness.io" api_key: "${HARNESS_API_KEY}" mcp: enabled: true tools: - harness_get_connector - harness_list_pipelines - harness_get_execution jira_connector_ref: "jira_connector" sync: auto_create_issues: true auto_transition: true environments: dev: "In Development" staging: "In QA" prod: "Released"
MCP Tool Usage
connector = harness_get_connector(connector_id="jira_connector", org_id="default", project_id="my_project") executions = harness_list_executions(pipeline_id="deploy_pipeline", limit=10) execution = harness_get_execution(execution_id="abc123", org_id="default", project_id="my_project")
REST API for PR Operations
Base URL
HARNESS_CODE_API="${HARNESS_BASE_URL}/code/api/v1"
Authentication
curl -H "x-api-key: ${HARNESS_API_KEY}"
-H "Content-Type: application/json"
"${HARNESS_CODE_API}/repos/{repo-ref}/pullreq/{pr-number}/comments"
Key Endpoints
Operation Method Endpoint
Create Comment POST /v1/repos/{repo}/pullreq/{pr}/comments
Create Code Comment POST /v1/repos/{repo}/pullreq/{pr}/comments (with path , line_start , line_end )
Submit Review POST /v1/repos/{repo}/pullreq/{pr}/reviews
Add Reviewer POST /v1/repos/{repo}/pullreq/{pr}/reviewers
Merge PR POST /v1/repos/{repo}/pullreq/{pr}/merge
Create General Comment
curl -X POST "${HARNESS_CODE_API}/repos/${REPO}/pullreq/${PR}/comments"
-H "x-api-key: ${HARNESS_API_KEY}"
-H "Content-Type: application/json"
-d '{"text": "Great work!"}'
Create Code Comment
curl -X POST "${HARNESS_CODE_API}/repos/${REPO}/pullreq/${PR}/comments"
-H "x-api-key: ${HARNESS_API_KEY}"
-H "Content-Type: application/json"
-d '{
"text": "Consider adding null check",
"path": "src/auth.ts",
"line_start": 42,
"line_end": 45,
"line_start_new": true,
"line_end_new": true
}'
Submit Review
curl -X POST "${HARNESS_CODE_API}/repos/${REPO}/pullreq/${PR}/reviews"
-H "x-api-key: ${HARNESS_API_KEY}"
-H "Content-Type: application/json"
-d '{"commit_sha": "abc123", "decision": "approved"}'
Decision Values: approved , changereq , reviewed
Merge PR
curl -X POST "${HARNESS_CODE_API}/repos/${REPO}/pullreq/${PR}/merge"
-H "x-api-key: ${HARNESS_API_KEY}"
-H "Content-Type: application/json"
-d '{
"method": "squash",
"source_sha": "abc123",
"title": "feat: Add auth",
"delete_source_branch": true
}'
Merge Methods: merge , squash , rebase , fast-forward
Bash Helper Functions
export HARNESS_CODE_API="${HARNESS_BASE_URL:-https://app.harness.io}/code/api/v1"
harness_pr_comment() {
local repo="$1" pr="$2" text="$3"
curl -s -X POST "${HARNESS_CODE_API}/repos/${repo}/pullreq/${pr}/comments"
-H "x-api-key: ${HARNESS_API_KEY}"
-H "Content-Type: application/json"
-d "{"text": "${text}"}"
}
harness_pr_approve() {
local repo="$1" pr="$2" commit_sha="$3"
curl -s -X POST "${HARNESS_CODE_API}/repos/${repo}/pullreq/${pr}/reviews"
-H "x-api-key: ${HARNESS_API_KEY}"
-H "Content-Type: application/json"
-d "{"commit_sha": "${commit_sha}", "decision": "approved"}"
}
harness_pr_merge() {
local repo="$1" pr="$2" method="${3:-squash}" source_sha="$4" title="$5"
curl -s -X POST "${HARNESS_CODE_API}/repos/${repo}/pullreq/${pr}/merge"
-H "x-api-key: ${HARNESS_API_KEY}"
-H "Content-Type: application/json"
-d "{"method": "${method}", "source_sha": "${source_sha}", "title": "${title}", "delete_source_branch": true}"
}
Python Client
import requests, os from typing import Optional, Literal
class HarnessCodeAPI: def init(self, api_key: str = None, base_url: str = None): self.api_key = api_key or os.environ.get("HARNESS_API_KEY") self.base_url = base_url or os.environ.get("HARNESS_BASE_URL", "https://app.harness.io") self.api_url = f"{self.base_url}/code/api/v1" self.headers = {"x-api-key": self.api_key, "Content-Type": "application/json"}
def create_comment(self, repo: str, pr_number: int, text: str, path: Optional[str] = None,
line_start: Optional[int] = None, line_end: Optional[int] = None,
parent_id: Optional[int] = None) -> dict:
url = f"{self.api_url}/repos/{repo}/pullreq/{pr_number}/comments"
data = {"text": text}
if parent_id:
data["parent_id"] = parent_id
elif path and line_start:
data.update({"path": path, "line_start": line_start, "line_end": line_end or line_start,
"line_start_new": True, "line_end_new": True})
return requests.post(url, headers=self.headers, json=data).json()
def submit_review(self, repo: str, pr_number: int, commit_sha: str,
decision: Literal["approved", "changereq", "reviewed"]) -> dict:
url = f"{self.api_url}/repos/{repo}/pullreq/{pr_number}/reviews"
data = {"commit_sha": commit_sha, "decision": decision}
return requests.post(url, headers=self.headers, json=data).json()
def approve(self, repo: str, pr_number: int, commit_sha: str) -> dict:
return self.submit_review(repo, pr_number, commit_sha, "approved")
def merge(self, repo: str, pr_number: int, source_sha: str,
method: Literal["merge", "squash", "rebase", "fast-forward"] = "squash",
title: Optional[str] = None, delete_source_branch: bool = True,
dry_run: bool = False) -> dict:
url = f"{self.api_url}/repos/{repo}/pullreq/{pr_number}/merge"
data = {"method": method, "source_sha": source_sha, "delete_source_branch": delete_source_branch, "dry_run": dry_run}
if title:
data["title"] = title
return requests.post(url, headers=self.headers, json=data).json()
Multi-Repository Workspace Support
Configuration
harness: workspace: repositories: - identifier: frontend-app path: ./frontend jira_project: FRONT - identifier: backend-api path: ./backend jira_project: BACK auto_create_repos: true default_branch: main review: cross_repo_review: true jira: sync_enabled: true aggregate_prs: true
Python API
from lib.harness_code_api import HarnessCodeAPI
client = HarnessCodeAPI() repos = client.setup_workspace_repos([ {"identifier": "frontend", "path": "./frontend"}, {"identifier": "backend", "path": "./backend"} ]) prs = client.get_workspace_prs(repo_identifiers=["frontend", "backend"], state="open", jira_key="PROJ-123")
Repository Creation
Python
repo = client.create_repository( identifier="my-service", description="User management service", default_branch="main", is_public=False, readme=True, license="MIT" )
REST API
Operation Method Endpoint
List Repos GET /v1/repos
Get Repo GET /v1/repos/{repo}
Create Repo POST /v1/repos
Update Repo PATCH /v1/repos/{repo}
Delete Repo DELETE /v1/repos/{repo}
Confluence Documentation Integration
Auto-Documentation
from lib.confluence_doc_linker import ConfluenceDocLinker
linker = ConfluenceDocLinker() docs = linker.ensure_issue_docs("PROJ-123") linker.link_readme_to_confluence(readme_path="./README.md", jira_key="PROJ-123")
Configuration
documentation: confluence: base_url: "${CONFLUENCE_BASE_URL}" space_key: "ENG" auto_create: enabled: true on_work_start: true readme: auto_update: true
Troubleshooting
Issue Solution
Invalid API Key Regenerate in Harness UI
Network timeout Check delegate connectivity
Permission denied Verify API key permissions
Jira unreachable Check firewall/proxy
Debug Logging
export HARNESS_LOG_LEVEL=debug export MCP_DEBUG=true
Best Practices
-
Use Harness Secrets for credentials
-
Select delegates with direct Jira network access
-
Configure error handling with retries
-
Enable logging for all operations
-
Scope API tokens to minimum permissions
Related Resources
-
Harness MCP Server
-
Harness Code Repository
-
PR Review
-
PR Merge
-
Jira Connector Setup
-
Jira Steps in CD