Claude-skill-registry harness-mcp
Harness MCP (Model Context Protocol) server integration for AI-powered CD operations, pipeline management, Git repositories, pull requests, code review comments, and bidirectional Jira synchronization
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/harness-mcp" ~/.claude/skills/majiayu000-claude-skill-registry-harness-mcp && rm -rf "$T"
manifest:
skills/data/harness-mcp/SKILL.mdsource content
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 | , , | Manage connectors |
| Pipelines | , , | Pipeline operations |
| Executions | , , | Track executions |
| Dashboards | , | Dashboard data |
| Repos | , | Repository management |
| Pull Requests | , , , , | 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\n[PROJ-123](https://company.atlassian.net/browse/PROJ-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\n**PR:** [#{42}]({pr.url})\n**Status:** {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 | |
| Create Code Comment | POST | (with , , ) |
| Submit Review | POST | |
| Add Reviewer | POST | |
| Merge PR | POST | |
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 | |
| Get Repo | GET | |
| Create Repo | POST | |
| Update Repo | PATCH | |
| Delete Repo | DELETE | |
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