ClaudeCodeMod

All shelves / MCP servers

Harness

harness/mcp-server · 105 stars · TypeScript · MIT

MCP server This is the official repo for the Harness MCP server

Install

The repo has no one-line install. Follow its README.

Open the repo

Files

README.md

Harness MCP Server 2.0

MCP Toplist

An MCP (Model Context Protocol) server that gives AI agents full access to the Harness.io platform through 11 consolidated tools and 261 resource types.

Why Use This MCP Server

Most MCP servers map one tool per API endpoint. For a platform as broad as Harness, that means 240+ tools — and LLMs get worse at tool selection as the count grows. Context windows fill up with schemas, and every new endpoint means new code.

This server is built differently:

  • 11 tools, 261 resource types. A registry-based dispatch system routes harness_list, harness_get, harness_create, etc. to any Harness resource — pipelines, services, environments, orgs, projects, feature flags, cost data, and more. The LLM picks from 11 tools instead of hundreds.
  • Full platform coverage. 42 default toolsets spanning CI/CD, GitOps, Feature Flags, Cloud Cost Management, Security Testing, Chaos Engineering, Database DevOps, Internal Developer Portal, Software Supply Chain, Infrastructure as Code Management, Release Management, Governance, Service Overrides, Knowledge Graph, and more. Opt-in Ansible and observability-evaluation coverage is available when needed.
  • Multi-project workflows out of the box. Agents discover organizations and projects dynamically — no hardcoded env vars needed. Ask "show failed executions across all projects" and the agent can navigate the full account hierarchy.
  • 35 prompt templates. Pre-built prompts for common workflows: build & deploy apps end-to-end, debug failed pipelines, review DORA metrics, triage vulnerabilities, optimize cloud costs, audit access control, plan feature flag rollouts, review pull requests, approve pending pipelines, and more.
  • Works everywhere. Stdio transport for local clients (Claude Desktop, Cursor, Devin Desktop), HTTP transport for remote/shared deployments, Docker and Kubernetes ready.
  • Zero-config start. Just provide a Harness API key. Account ID is auto-extracted from PAT and SAT tokens, org/project defaults are optional, and toolset filtering lets you expose only what you need.
  • Extensible by design. Adding a new Harness resource means adding a declarative data file — no new tool registration, no schema changes, no prompt updates.

Prerequisites

Before installing or running the server, you need a Harness API key:

  1. Log in to your Harness account
  2. Go to My Profile → API Keys → + New API Key
  3. Create a new Token under the API key — this generates a PAT or SAT in the format <prefix>.<accountId>.<tokenId>.<secret>
  4. Save the token somewhere secure — you'll need it in the next step

For detailed instructions, see the Harness API Quickstart.

Quick Start

Option 0: Hosted Harness MCP

If your Harness account has the hosted MCP service enabled, clients that support remote MCP servers can connect directly to the managed endpoint instead of running the server locally.

Important: The hosted MCP service uses Harness Platform OAuth, not HARNESS_API_KEY. It must also be enabled/configured per account by Harness Support before the endpoint can be used.

See Hosted Harness MCP for configuration examples.

Option 1: npx (Recommended)

No install required — just run it:

HARNESS_API_KEY=pat.xxx.xxx.xxx npx harness-mcp-v2@latest

Or configure the API key in your AI client (see Client Configuration below).

# Stdio transport (default — for Claude Desktop, Cursor, Devin Desktop, etc.)
HARNESS_API_KEY=pat.xxx npx harness-mcp-v2

# HTTP transport (for remote/shared deployments)
HARNESS_API_KEY=pat.xxx npx harness-mcp-v2 http --port 8080

Note: The account ID is auto-extracted from PAT and SAT tokens (pat.<accountId>... or sat.<accountId>...), so HARNESS_ACCOUNT_ID is only needed for API keys without an embedded account segment.

Option 2: Global Install

npm install -g harness-mcp-v2

# Then run directly
harness-mcp-v2

Option 3: Build from Source

For development or customization:

git clone https://github.com/harness/mcp-server.git
cd mcp-server
pnpm install
pnpm build

# Run
pnpm start              # Stdio transport
pnpm start:http         # HTTP transport
pnpm inspect            # Test with MCP Inspector

Anthropic MCP Directory bundle

The MCPB bundle manifest lives in [mcp-directory/](mcp-directory/), and the 512×512 bundle icon is tracked at [icon.png](icon.png) in the repository root. The packaged archive contains root-level manifest.json, icon.png, server/, package.json, npm-shrinkwrap.json, and production node_modules/.

To keep the archive small, build MCPB packages from a staging directory:

pnpm prepare:mcpb

The staging directory is written to dist/mcpb/ with production dependencies installed from npm-shrinkwrap.json using npm's flat layout. The pinned official MCPB CLI validates it and creates dist/harness-mcp-server-<version>.mcpb.

Version tags matching v*.*.* publish that bundle to the corresponding GitHub Release automatically. To backfill an existing release without republishing npm, run the Release workflow manually with its release_tag input (for example, v3.2.20). The workflow checks out and builds that exact tag before replacing only its versioned MCPB asset.

CLI Usage

harness-mcp-v2 [stdio|http] [--port <number>]

Options:
  --port <number>  Port for HTTP transport (default: 3000, or PORT env var)
  --help           Show help message and exit
  --version        Print version and exit

Transport defaults to stdio if not specified. Use http for remote/shared deployments.

HTTP Transport

When running in HTTP mode, the server exposes:

Endpoint Method Description
/mcp POST MCP JSON-RPC endpoint (initialize + session requests)
/mcp GET SSE stream for server-initiated messages (progress, elicitation)
/mcp DELETE Terminate an active MCP session
/mcp OPTIONS CORS preflight
/health GET Health check — returns { "status": "ok", "sessions": <count> }
/.well-known/oauth-protected-resource GET RFC 9728 metadata when HARNESS_MCP_MODE=oauth
/.well-known/oauth-protected-resource/mcp GET Path-aware RFC 9728 metadata for the default /mcp resource

The HTTP transport runs in session-based mode. A new MCP session is created on initialize, the server returns an mcp-session-id header, and subsequent requests for that session must include the same header.

Operational constraints in HTTP mode:

  • Set HARNESS_MCP_AUTH_TOKEN for shared or remotely reachable single-user and multi-user deployments. When set, every POST, GET, and DELETE request to /mcp must include Authorization: Bearer <token>.
  • OAuth mode accepts HarnessID access tokens instead of HARNESS_MCP_AUTH_TOKEN and can bind to a non-loopback address without the unauthenticated opt-out.
  • Non-loopback single-user and multi-user binds require HARNESS_MCP_AUTH_TOKEN by default. To run unauthenticated on a non-loopback interface anyway, set HARNESS_MCP_ALLOW_UNAUTHENTICATED_HTTP=true explicitly.
  • POST /mcp without mcp-session-id must be an initialize request.
  • POST /mcp, GET /mcp, and DELETE /mcp for existing sessions require the mcp-session-id header.
  • GET /mcp is used for SSE notifications (progress updates and elicitation prompts).
  • Idle sessions are reaped after MCP_SESSION_TTL_MS milliseconds once no request or SSE stream is active (default 1800000, or 30 minutes).
  • GET /health is the only non-MCP endpoint.
  • Request body size is capped by HARNESS_MAX_BODY_SIZE_MB (default 10 MB).
  • Set x-harness-pipeline-version: 0 or 1 on the initialize request to select V0 or V1 pipeline resources for that HTTP session.
  • Set x-harness-auto-approve-risk: none|low_write|medium_write|high_write|all on the initialize request to choose a stricter per-session auto-approval threshold. The server caps this value at the deployment-level HARNESS_AUTO_APPROVE_RISK, so a session can reduce but not expand the configured approval ceiling.

HarnessID OAuth Mode

Set HARNESS_MCP_MODE=oauth to let remote MCP clients discover HarnessID and complete OAuth 2.1 Authorization Code with PKCE. OAuth mode is available only with HTTP transport. Production HarnessID, MCP resource, and API routing defaults are built in:

HARNESS_MCP_MODE=oauth

This defaults to the issuer https://id.harness.io/idp/realms/HarnessIDP, resource https://mcp.harness.io/mcp, OAuth client mcp-client, and Harness API base https://mcp.harness.io/cli. Override them only for QA, local development, or another Harness environment.

HARNESS_API_KEY must not be set in this mode. HARNESS_MCP_OAUTH_JWKS_URI defaults to <issuer>/protocol/openid-connect/certs, and HARNESS_ACCOUNT_ID is unnecessary because the account comes from the token.

The server publishes RFC 9728 protected-resource metadata and returns this challenge when a client has not authenticated:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.harness.io/.well-known/oauth-protected-resource/mcp"

It validates the HarnessID access token's RS256 signature, iss, expiry, and sub using the configured JWKS endpoint, and checks that the token was issued to HARNESS_MCP_OAUTH_CLIENT_ID through the azp claim. HARNESS_MCP_OAUTH_RESOURCE is the RFC 9728 protected-resource identifier used for discovery and challenges. Current HarnessID access tokens use aud: account rather than the MCP URL, so the resource is not compared with aud.

The account ID comes from the token's HARNESS_MCP_OAUTH_ACCOUNT_CLAIM claim (account_id by default), which the HarnessID organization scope populates. Each session stores the caller's access token and forwards it to the Harness API as Authorization: Bearer, so Harness RBAC and audit records reflect the logged-in user rather than a shared PAT. The session is bound to the sub and account it was created with: a later request may carry a refreshed token, but one for a different user or account is rejected.

Clients normally need only the MCP resource URL:

{
  "mcpServers": {
    "harness": {
      "url": "https://mcp.harness.io/mcp"
    }
  }
}

The client reads the protected-resource metadata, discovers HARNESS_MCP_OAUTH_ISSUER, and then uses that authorization server's RFC 8414 metadata. If the client does not support dynamic client registration, use the pre-registered mcp-client client ID.

See HarnessID OAuth for a self-hosted MCP server for the QA Keycloak checklist and validation commands.

Multi-User Mode

Set HARNESS_MCP_MODE=multi-user for shared HTTP deployments where each client authenticates as a different Harness user. In this mode:

  • HARNESS_API_KEY must not be set in the server config — the server holds no Harness credentials.
  • Each session must provide x-harness-api-key on the initialize request. x-harness-account-id is required only when the API key does not embed an account segment.
  • Sessions may also provide x-harness-org and x-harness-project headers to set default scope for that session.
  • The Harness API key flows through to every Harness API call for that session, so the audit trail in Harness reflects the real user.
  • HARNESS_MCP_AUTH_TOKEN is independent and can still be used as an additional transport-layer gate.
# Health check
curl http://localhost:3000/health

# MCP initialize request (capture mcp-session-id response header)
# In multi-user mode, x-harness-api-key is required on initialize.
# x-harness-account-id is needed only for API keys without an embedded account segment.
curl -i -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer $HARNESS_MCP_AUTH_TOKEN" \
  -H "x-harness-api-key: $HARNESS_API_KEY" \
  -H "x-harness-account-id: $HARNESS_ACCOUNT_ID" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'

# Subsequent MCP request (use returned session ID)
curl -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer $HARNESS_MCP_AUTH_TOKEN" \
  -H "mcp-session-id: <session-id>" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

# Terminate session
curl -X DELETE http://localhost:3000/mcp \
  -H "Authorization: Bearer $HARNESS_MCP_AUTH_TOKEN" \
  -H "mcp-session-id: <session-id>"

HARNESS_MCP_ALLOWED_HOSTS controls Host-header validation for DNS-rebinding protection, and CORS limits browser origins. Neither is authentication; use HARNESS_MCP_AUTH_TOKEN or an authenticated gateway/reverse proxy for access control.

Client Configuration

Note: HARNESS_ORG and HARNESS_PROJECT are optional. They set the org ID and project ID used when not specified per tool call. Agents can discover orgs and projects dynamically using harness_list(resource_type="organization") and harness_list(resource_type="project"). The deprecated names HARNESS_DEFAULT_ORG_ID and HARNESS_DEFAULT_PROJECT_ID are still accepted for backward compatibility.

Hosted Harness MCP

Harness also supports a hosted MCP endpoint for accounts that have the managed service enabled. This is useful when you want a shared remote MCP endpoint instead of running npx harness-mcp-v2 or self-hosting the HTTP transport yourself.

Important: Hosted MCP authentication uses Harness Platform OAuth. It does not use HARNESS_API_KEY in the client config. Hosted MCP availability is configured per Harness account, so you will need to work with Harness Support to enable/configure the setting before using it.

The hosted endpoint https://mcp.harness.io/mcp is a managed service. Client-side MCP config in Claude, Cursor, or Cowork cannot override which Harness environment it routes to. For Harness0 or another private Harness SaaS environment, ask Harness Support to enable/configure hosted MCP for that environment, or run the local/self-hosted server and set HARNESS_BASE_URL to the target Harness host.

Hosted MCP example:

{
  "mcpServers": {
    "harness-prod1-mcp": {
      "url": "https://mcp.harness.io/mcp",
      "auth": {
        "CLIENT_ID": "mcp-client"
      }
    }
  }
}

Example with both hosted and local entries:

{
  "mcpServers": {
    "harness-hosted": {
      "url": "https://mcp.harness.io/mcp",
      "auth": {
        "CLIENT_ID": "mcp-client"
      }
    },
    "harness-local": {
      "command": "/absolute/path/to/npx",
      "args": ["-y", "harness-mcp-v2@latest"],
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx",
        "PATH": "/directory/containing/node:/usr/local/bin:/usr/bin:/bin"
      }
    }
  }
}

Troubleshooting npx ENOENT or node: No such file or directory

This is a client process-launch failure, not a Harness authentication failure. The MCP server has not started yet, so changing HARNESS_API_KEY will not affect spawn npx ENOENT.

GUI apps (Cursor, Claude Desktop, Devin Desktop, VS Code) don't always inherit your shell's PATH, so they can fail to find npx or node after a config reload. Fix this by using absolute paths and explicitly setting PATH in the env block:

{
  "mcpServers": {
    "harness": {
      "command": "/absolute/path/to/npx",
      "args": ["-y", "harness-mcp-v2"],
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx",
        "PATH": "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
      }
    }
  }
}

Find your paths with which npx and which node in a terminal, then make sure the directory containing node is included in the PATH value above. Common locations:

  • Homebrew (macOS): /opt/homebrew/bin/npx
  • nvm: ~/.nvm/versions/node/v20.x.x/bin/npx (run nvm which current to find the exact path)
  • System Node: /usr/local/bin/npx

Claude Desktop (claude_desktop_config.json)

npx (zero install)

{
  "mcpServers": {
    "harness": {
      "command": "/absolute/path/to/npx",
      "args": ["-y", "harness-mcp-v2@latest"],
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx",
        "PATH": "/directory/containing/node:/usr/local/bin:/usr/bin:/bin"
      }
    }
  }
}

node (local install)

npm install -g harness-mcp-v2
{
  "mcpServers": {
    "harness": {
      "command": "/absolute/path/to/harness-mcp-v2",
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx",
        "PATH": "/directory/containing/node:/usr/local/bin:/usr/bin:/bin"
      }
    }
  }
}

Claude Code (via claude mcp add)

npx (zero install)

claude mcp add harness -- npx harness-mcp-v2

node (local install)

npm install -g harness-mcp-v2
claude mcp add harness -- harness-mcp-v2

Then set HARNESS_API_KEY in your environment or .env file.

Cursor (.cursor/mcp.json)

npx (zero install, recommended for local Cursor configs)

{
  "mcpServers": {
    "harness": {
      "command": "/absolute/path/to/npx",
      "args": ["-y", "harness-mcp-v2@latest"],
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx",
        "PATH": "/directory/containing/node:/usr/local/bin:/usr/bin:/bin"
      }
    }
  }
}

Run which npx in a terminal and use that full path for command; include the directory from which node at the front of PATH.

node (local install)

npm install -g harness-mcp-v2
{
  "mcpServers": {
    "harness": {
      "command": "/absolute/path/to/harness-mcp-v2",
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx",
        "PATH": "/directory/containing/node:/usr/local/bin:/usr/bin:/bin"
      }
    }
  }
}

Run which harness-mcp-v2 after npm install -g harness-mcp-v2 and use that full path for command; include the directory from which node at the front of PATH.

Devin Desktop (~/.windsurf/mcp.json)

npx (zero install)

{
  "mcpServers": {
    "harness": {
      "command": "/absolute/path/to/npx",
      "args": ["-y", "harness-mcp-v2@latest"],
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx",
        "PATH": "/directory/containing/node:/usr/local/bin:/usr/bin:/bin"
      }
    }
  }
}

node (local install)

npm install -g harness-mcp-v2
{
  "mcpServers": {
    "harness": {
      "command": "/absolute/path/to/harness-mcp-v2",
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx",
        "PATH": "/directory/containing/node:/usr/local/bin:/usr/bin:/bin"
      }
    }
  }
}

Using a local build from source?

Replace the command with the path to your built index.js:

{
  "command": "node",
  "args": ["/absolute/path/to/harness-mcp-v2/build/index.js", "stdio"]
}

MCP Gateway

The Harness MCP server is fully compatible with MCP Gateways — reverse proxies that provide centralized authentication, governance, tool routing, and observability across multiple MCP servers. Since the server implements the standard MCP protocol with both stdio and HTTP transports, it works behind any MCP-compliant gateway with no code changes.

Why use a gateway?

  • Centralized credential management — no API keys in agent configs
  • Governance & audit logging for all tool calls across teams
  • Single endpoint for agents instead of N connections to N MCP servers
  • Access control — restrict which teams can use which tools

Docker MCP Gateway

Register the server in your Docker MCP Gateway configuration:

{
  "mcpServers": {
    "harness": {
      "command": "npx",
      "args": ["harness-mcp-v2"],
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx"
      }
    }
  }
}

Portkey

Add the Harness MCP server to your Portkey MCP Gateway for enterprise governance, cost tracking, and multi-LLM routing:

{
  "mcpServers": {
    "harness": {
      "command": "npx",
      "args": ["harness-mcp-v2"],
      "env": {
        "HARNESS_API_KEY": "pat.xxx.xxx.xxx"
      }
    }
  }
}

LiteLLM

Add to your LiteLLM proxy config:

mcp_servers:
  - name: harness
    command: npx
    args:
      - harness-mcp-v2
    env:
      HARNESS_API_KEY: "pat.xxx.xxx.xxx"

Envoy AI Gateway

The server works with Envoy AI Gateway's MCP support via HTTP transport:

# Start the server in HTTP mode
HARNESS_API_KEY=pat.xxx.xxx.xxx npx harness-mcp-v2 http --port 8080

Then configure Envoy to route to http://localhost:8080/mcp as an upstream MCP backend.

Kong

Use Kong's AI MCP Proxy plugin to expose the Harness MCP server through your existing Kong gateway infrastructure.

Other Gateways

Any gateway that supports the MCP specification (Microsoft MCP Gateway, IBM ContextForge, Cloudflare Workers, etc.) can proxy this server. For stdio-based gateways, use the default transport. For HTTP-based gateways, start the server with http transport and point the gateway at the /mcp endpoint.

Docker

Build and run the server as a Docker container:

# Build the image
pnpm docker:build

# Run with your .env file
pnpm docker:run

# Or run directly with env vars
docker run --rm -p 3000:3000 \
  -e HARNESS_API_KEY=pat.xxx.xxx.xxx \
  -e HARNESS_ACCOUNT_ID=your-account-id \
  harness-mcp-server

The container runs in HTTP mode on port 3000 by default with a built-in health check.

Kubernetes

Deploy to a Kubernetes cluster using the provided manifests:

# 1. Edit the Secret with your real credentials
#    k8s/secret.yaml — replace HARNESS_API_KEY and HARNESS_ACCOUNT_ID

# 2. Apply all manifests
kubectl apply -f k8s/

# 3. Verify the deployment
kubectl -n harness-mcp get pods

# 4. Port-forward for local testing
kubectl -n harness-mcp port-forward svc/harness-mcp-server 3000:80
curl http://localhost:3000/health

The deployment runs 2 replicas with readiness/liveness probes, resource limits, and non-root security context. The Service exposes port 80 internally (targeting container port 3000).

Configuration

The server automatically loads environment variables from a .env file in the project root if one exists. Copy .env.example to .env and fill in your values. Environment variables can also be set via your shell or MCP client config.

Variable Required Default Description
HARNESS_MCP_MODE No single-user Deployment mode: single-user (shared API key), multi-user (HTTP with per-session API keys), or oauth (HTTP with HarnessID access-token validation)
HARNESS_API_KEY Yes* -- Harness personal access token or service account token. Required in single-user mode. Must NOT be set in multi-user or oauth mode, where each session brings its own credential
HARNESS_ACCOUNT_ID No (from PAT/SAT) Harness account identifier. Auto-extracted from PAT/SAT tokens in single-user mode; multi-user sessions can provide their own via x-harness-account-id when the API key does not embed one
HARNESS_BASE_URL No https://app.harness.io (https://mcp.harness.io/cli in OAuth mode) Harness API/UI base URL. OAuth mode routes through the hosted MCP /cli proxy by default; other modes use the Harness SaaS API directly
HARNESS_MCP_OAUTH_ISSUER No https://id.harness.io/idp/realms/HarnessIDP HarnessID issuer matched exactly against the access token iss claim
HARNESS_MCP_OAUTH_RESOURCE No https://mcp.harness.io/mcp Public canonical MCP URL published as the RFC 9728 resource identifier
HARNESS_MCP_OAUTH_JWKS_URI No <issuer>/protocol/openid-connect/certs HarnessID JWKS endpoint used to validate RS256 access-token signatures
HARNESS_MCP_OAUTH_CLIENT_ID No mcp-client HarnessID client the access token must be issued to, checked against the token's azp claim
HARNESS_MCP_OAUTH_ACCOUNT_CLAIM No account_id Access-token claim carrying the Harness account ID, populated by the HarnessID organization scope
HARNESS_MCP_OAUTH_SCOPES No openid profile email organization Space-separated scopes advertised in RFC 9728 protected-resource metadata
HARNESS_FME_API_KEY No -- Optional single-user/self-hosted FME/Split Admin credential used for fme_ resources in legacy (workspace_id) mode only. Legacy FME is unavailable in OAuth mode so HarnessID tokens are never sent to api.split.io; use Harness-native org_id+project_id scope instead. Must not be set in multi-user or oauth mode
HARNESS_FME_BASE_URL No https://api.split.io Split/FME Admin API base URL used by fme_ resources in legacy (workspace_id) mode only. HTTP URLs require HARNESS_ALLOW_HTTP=true for local development. Harness-native (org_id+project_id) mode ignores this and uses the standard HARNESS_API_KEY/HARNESS_BASE_URL instead
HARNESS_ORG No -- Organization ID. Used when org_id is not specified per tool call. If omitted, org_id must be provided explicitly. Agents can also discover orgs dynamically via harness_list(resource_type="organization")
HARNESS_PROJECT No -- Project ID. Used when project_id is not specified per tool call. Agents can also discover projects dynamically via harness_list(resource_type="project")
HARNESS_API_TIMEOUT_MS No 30000 HTTP request timeout in milliseconds
HARNESS_MAX_RETRIES No 3 Retry count for transient failures (429, 5xx)
HARNESS_MAX_BODY_SIZE_MB No 10 Max HTTP request body size in MB for http transport
HARNESS_RATE_LIMIT_RPS No 10 Client-side request throttle (requests per second) to Harness APIs
LOG_LEVEL No info Log verbosity: debug, info, warn, error
HARNESS_TOOLSETS No (defaults) Comma-separated toolset list. Empty loads default toolsets. Supports +name to explicitly include opt-in toolsets and -name to remove defaults (see Toolset Filtering)
HARNESS_READ_ONLY No false Block all mutating operations (create, update, delete, execute). Only list and get are allowed. Useful for shared/demo environments
HARNESS_AUTO_APPROVE_RISK No none Risk-based auto-approve threshold for autonomous workflows. Operations at or below this risk proceed without confirmation. Values: none, low_write, medium_write, high_write, all. See Elicitation
HARNESS_SKIP_ELICITATION No false Deprecated — use HARNESS_AUTO_APPROVE_RISK=all instead. Kept for backward compatibility
HARNESS_ALLOW_HTTP No false Allow non-HTTPS HARNESS_BASE_URL. By default, the server enforces HTTPS for security. Set to true only for local development against a non-TLS Harness instance
HARNESS_PIPELINE_VERSION No 0 (Alpha) Pipeline YAML version. 0 loads the pipeline resource type and excludes pipeline_v1; 1 loads pipeline_v1 and excludes pipeline. HTTP sessions can override this at initialize time with x-harness-pipeline-version: 0 or 1
HARNESS_MCP_ALLOWED_HOSTS No -- Comma-separated hostnames allowed by HTTP transport Host-header validation. mcp.harness.io is allowed by default for localhost binds; add proxy/custom domains here
HARNESS_MCP_AUTH_TOKEN No -- Static Bearer token required on /mcp HTTP routes when set. Required by default for non-loopback single-user and multi-user binds. Must be unset in oauth mode
HARNESS_MCP_ALLOW_UNAUTHENTICATED_HTTP No false Explicitly allow unauthenticated HTTP transport on non-loopback binds. Use only behind another authenticated control
HARNESS_MCP_TRUST_PROXY No 0 Number of reverse proxy / load balancer hops to trust for client IP resolution (Express trust proxy). Set to the proxy count in front of the server so per-IP rate limiting keys on the real client rather than the proxy socket peer
HARNESS_MCP_LOG_FILE No ~/.claude/harness-mcp.log File used for stdio disconnect/crash diagnostics when stderr may no longer be available
HARNESS_LOG_UNSAFE_BODIES No false Include raw request/response bodies in logs. Off by default since bodies can contain secrets; enable only for local debugging
HARNESS_AUDIT_FILE No -- Append audit events to a newline-delimited JSON file for durable local collection
HARNESS_AUDIT_WEBHOOK_URL No -- HTTPS endpoint that receives batched audit events. HTTP URLs require HARNESS_ALLOW_HTTP=true for local development
HARNESS_AUDIT_WEBHOOK_TOKEN No -- Optional bearer token sent to the audit webhook
HARNESS_AUDIT_WEBHOOK_BATCH_SIZE No 10 Number of audit events to batch before webhook flush
HARNESS_AUDIT_WEBHOOK_FLUSH_MS No 5000 Max time to hold audit events before webhook flush
OTEL_EXPORTER_OTLP_ENDPOINT No -- Enables OpenTelemetry audit spans when the optional OpenTelemetry packages are installed
HARNESS_SEARCH_PROVIDER No local Semantic search backend: local (in-process ONNX embeddings, default), remote (external search service via HTTP, required for multi-user mode), or none (disable semantic search, fall back to keyword scatter-gather only). Use none in air-gapped environments or when startup model loading is undesirable
HARNESS_SEARCH_SERVICE_URL No -- Base URL of the remote search service when HARNESS_SEARCH_PROVIDER=remote (e.g. http://search-svc:8080). Required when using the remote provider
HARNESS_SEARCH_SERVICE_HEADERS No -- JSON object of headers sent with every request to the remote search service. Supports any auth scheme: {"Authorization":"Bearer tok"}, {"x-api-key":"key"}, or multiple internal service-to-service headers
HARNESS_HF_CACHE_DIR No /tmp/hf-cache Directory for the @huggingface/transformers model cache used by the local search provider. The Docker image pre-bakes the model into /app/.cache/hf to avoid runtime downloads. Set to a persistent volume path in production deployments
HARNESS_DIAGNOSE_LOG_FETCH_CONCURRENCY No 3 Max concurrent log-blob downloads issued by harness_diagnose when fetching logs for failed steps. Increase only if diagnose latency is dominated by log-fetch wall-clock and the pod has memory headroom

Semantic Search

harness_search uses semantic routing to narrow scatter-gather API calls before fanning out to Harness. Three search providers are available:

Provider When to use
local (default) Single-user stdio mode. Runs all-MiniLM-L6-v2 in-process via @huggingface/transformers. Downloads ~23 MB model on first use; subsequent starts use the cache.
remote Multi-user HTTP mode (Harness-hosted). Delegates embedding and retrieval to an external search service. Tenant isolation is enforced via tenant_id — static knowledge/docs use global, per-account entity data uses the account ID.
none Disable semantic search entirely; falls back to keyword scatter-gather across all resource types.

Remote provider configuration:

HARNESS_SEARCH_PROVIDER=remote
HARNESS_SEARCH_SERVICE_URL=http://search-svc:8080

# Auth — any scheme via HARNESS_SEARCH_SERVICE_HEADERS (JSON object):
HARNESS_SEARCH_SERVICE_HEADERS='{"Authorization":"Bearer <token>"}'   # standard bearer
HARNESS_SEARCH_SERVICE_HEADERS='{"x-api-key":"<key>"}'               # API key header
HARNESS_SEARCH_SERVICE_HEADERS='{"x-harness-token":"<svc-token>"}'   # internal service-to-service
# Multiple headers (e.g. service mesh + tenant routing):
HARNESS_SEARCH_SERVICE_HEADERS='{"x-harness-token":"<tok>","x-tenant":"<id>"}'
# No auth (service mesh / mTLS handles it):
# omit HARNESS_SEARCH_SERVICE_HEADERS entirely

Testing the remote provider locally with the included stub service (no external dependencies):

# 1. Create a venv and install FastAPI
python3 -m venv .venv-stub
.venv-stub/bin/pip install fastapi uvicorn

# 2. Start the stub (in-memory, cosine similarity, corpus + tenant filtering)
.venv-stub/bin/uvicorn stub-search-service:app --port 8082

# 3. Build the MCP server
pnpm build

# 4. Run the integration smoke test
node test-remote-provider.mjs
# Expected output:
#   available: true
#   indexed 2 docs
#   entity search results: pipeline:ts-test score=... corpus=entities
#   knowledge search results: schema:trigger score=...
#   all-corpus search results: (merged, sorted by score)
#   isolation check (other-acct, should be empty): PASS

# 5. Tear down
kill $(lsof -ti :8082)

The stub (stub-search-service.py) implements the same /v1/health, /v1/ingest, and /v1/search contract as the production search service. It uses a simple bag-of-chars embedding so no model download is required — results are semantically plausible but not production-quality.

HTTPS Enforcement

HARNESS_BASE_URL must use HTTPS by default. If you set a non-HTTPS URL (e.g. http://localhost:8080), the server will refuse to start with:

HARNESS_BASE_URL must use HTTPS (got "http://..."). If you need HTTP for local development, set HARNESS_ALLOW_HTTP=true.

Audit Logging

All registry-dispatched Harness API operations (list, get, create, update, delete, and execute) emit structured audit events when audit sinks are configured. Mutating events include the confirmation path used by elicitation or auto-approval when a confirmation context is present; read events currently omit confirmation metadata. Local metadata and schema discovery tools that bypass the registry, such as harness_describe and harness_schema, are not part of this audit stream. A stderr sink is registered by default but goes through the normal logger and obeys LOG_LEVEL; configure file or webhook sinks for durable audit collection:

  • HARNESS_AUDIT_FILE appends newline-delimited JSON events for local collection.
  • HARNESS_AUDIT_WEBHOOK_URL posts { "events": [...] } batches to an HTTPS webhook, optionally with HARNESS_AUDIT_WEBHOOK_TOKEN. Failed batches are re-enqueued with bounded capacity and eventually dropped with a warning rather than blocking tool execution.
  • OTEL_EXPORTER_OTLP_ENDPOINT enables audit spans when the optional OpenTelemetry peer dependencies are installed. The sink reuses an existing tracer provider when one is registered, otherwise it bootstraps a standalone OTLP exporter.

Each event includes the tool name, resource type, operation, identifiers, timestamp, risk, outcome, HTTP method/path, duration, and confirmation method when applicable. Audit sinks are best-effort telemetry; delivery issues are logged and never replay or change the underlying Harness API operation. For OTel setup details and span attributes, see specs/005-otel-audit-sink.md.

Tools Reference

The server exposes 11 MCP tools. Most API tools accept org_id and project_id as optional overrides — if omitted, they fall back to HARNESS_ORG and HARNESS_PROJECT. harness_describe is local metadata only and does not use org/project scope.

URL support: Most API-facing tools accept a url parameter — paste a Harness UI URL and the server auto-extracts org, project, resource type, resource ID, pipeline ID, and execution ID. harness_describe does not accept url.

Scope support: Resource types with account/org/project variants expose supportedScopes in harness_describe. Pass resource_scope when you need a specific level:

  • resource_scope: "account" sends only accountIdentifier.
  • resource_scope: "org" sends accountIdentifier and orgIdentifier.
  • resource_scope: "project" sends account, org, and project identifiers.

Current multi-scope resources include connector, service, environment, infrastructure, secret, file_store, template, policy, and policy_set. If resource_scope is omitted, the registry uses the resource's default scope and configured defaults, except resources marked as optional scope may omit org/project unless explicitly passed. Harness URLs can also set the scope automatically when the path contains account-level or project-level context.

Structured output: Every tool declares an MCP outputSchema. harness_list normalizes list-like Harness responses into object-shaped structured content so strict clients can validate it: top-level arrays become { "items": [...], "total": <count>, "page": <page> }, and common wrapper keys such as content, data, body, objects, or features are hoisted to items when needed. The text response still contains the compact JSON payload returned to all clients.

Tool Description
harness_describe Discover available resource types, operations, and fields. No API call — returns local registry metadata.
harness_schema Fetch exact YAML/JSON Schema definitions and examples for creating/updating resources. Pipeline/template schemas are bundled; connector, environment, service, secret, and infrastructure schemas are scope-aware entity schemas fetched from bundled snapshots or NG /yaml-schema; release_process and release_activity schemas are fetched live from RMG /api/yamlSchema. Supports deep drilling via path.
harness_list List resources of a given type with filtering, search, and pagination.
harness_get Get a single resource by its identifier.
harness_create Create a new resource. Supports inline and remote (Git-backed) pipelines. Prompts for user confirmation via elicitation.
harness_update Update an existing resource. Supports inline and remote (Git-backed) pipelines. Prompts for user confirmation via elicitation.
harness_delete Delete a resource. Prompts for user confirmation via elicitation. Destructive.
harness_execute Execute an action on a resource (run/retry pipeline, import pipeline from Git, toggle flag, sync app). Prompts for user confirmation via elicitation. For pipeline runs, use the runtime-input workflow below (supports branch/tag/pr_number/commit_sha shorthand expansion).
harness_search Search across Harness resource types with a single query. Uses semantic routing (local all-MiniLM-L6-v2 ONNX embeddings, 384-dim) to predict relevant resource types from a knowledge corpus indexed at startup — typically narrowing from ~163 types to 1–8 before scatter-gather. Falls back to full keyword scatter-gather when semantic confidence is low. Response includes semantic_routed and types_skipped when routing fires. See docs/search-guidelines.md for how to make new resource types discoverable.
harness_diagnose Diagnose pipeline, connector, delegate, and gitops_application resources (aliases: execution -> pipeline, gitops_app -> gitops_application). For pipelines, returns stage/step timing and failure details; for connectors/delegates/GitOps apps, returns targeted health and troubleshooting signals.
harness_status Get a real-time project health dashboard — recent executions, failure rates, and deep links.

Schema Lookup Workflow

Use harness_schema before creating or updating YAML-backed resources so agents can copy exact field names and constraints instead of guessing from prose.

  • Bundled schemas include pipeline, template, trigger, pipeline_v1, template_v1, inputSet_v1, overlayInputSet_v1, and agent-pipeline.
  • Entity schemas include connector, environment, service, secret, and infrastructure. They are scope-aware (account, org, or project) and require org_id/project_id when the selected scope requires them.
  • Release Management definitions (release_process, release_activity) fetch live JSON Schema from RMG /api/yamlSchema (not bundled). Pass scope, org_id, and project_id when scoping to org or project.
  • Vendored entity snapshots are used first when they match the runtime account; otherwise the tool falls back to the Harness NG /yaml-schema API and caches the result.
  • Omit path for a field/section summary, then pass a dot-separated path to inspect a nested definition.

Examples:

{ "resource_type": "pipeline", "path": "pipeline.stages" }
{
  "resource_type": "connector",
  "scope": "project",
  "org_id": "default",
  "project_id": "payments"
}

Maintainers can refresh the vendored entity snapshots with pnpm sync-entity-schemas when Harness entity YAML schemas change.

Tool Examples

Discover what resources are available:

{ "resource_type": "pipeline" }

List organizations in the account:

{ "resource_type": "organization" }

List projects in an organization:

{ "resource_type": "project", "org_id": "default" }

List pipelines in a project:

{ "resource_type": "pipeline", "search_term": "deploy", "size": 10 }

Get a specific service:

{ "resource_type": "service", "resource_id": "my-service-id" }

Run a pipeline:

{
  "resource_type": "pipeline",
  "action": "run",
  "resource_id": "my-pipeline",
  "inputs": { "tag": "v1.2.3" },
  "wait": true
}

Toggle a feature flag:

{
  "resource_type": "feature_flag",
  "action": "toggle",
  "resource_id": "new_checkout_flow",
  "enable": true,
  "environment": "production"
}

Search across all resource types:

{ "query": "payment-service" }

Diagnose an execution by ID (summary mode — default):

{ "execution_id": "abc123XYZ" }

Diagnose from a Harness URL:

{ "url": "https://app.harness.io/ng/account/.../pipelines/myPipeline/executions/abc123XYZ/pipeline" }

Diagnose connector connectivity:

{ "resource_type": "connector", "resource_id": "my_github_connector" }

Diagnose delegate health:

{ "resource_type": "delegate", "resource_id": "delegate-us-east-1" }

Diagnose a GitOps application (with options):

{
  "resource_type": "gitops_application",
  "resource_id": "checkout-app",
  "options": { "agent_id": "gitops-agent-1" }
}

Get the latest execution report for a pipeline:

{ "pipeline_id": "my-pipeline" }

Full diagnostic mode with YAML and failed step logs:

{ "execution_id": "abc123XYZ", "summary": false }

Summary mode with logs enabled (best of both):

{ "execution_id": "abc123XYZ", "include_logs": true }

Get project health status:

{ "org_id": "default", "project_id": "my-project", "limit": 5 }

List database schemas filtered by migration type:

{ "resource_type": "database_schema", "migration_type": "Liquibase" }

List database instances for a schema:

{ "resource_type": "database_instance", "dbschema_id": "my_schema" }

Get the resolved LLM authoring pipeline for a schema and instance:

{ "resource_type": "database_llm_authoring_pipeline", "resource_id": "my_schema", "dbinstance_id": "prod_db" }

List snapshot object names (e.g. tables) for a schema instance:

{
  "resource_type": "database_snapshot_object",
  "dbschema_id": "my_schema",
  "dbinstance_id": "prod_db",
  "object_type": "Table"
}

Get full snapshot metadata for specific named objects:

{
  "resource_type": "database_snapshot_object",
  "resource_id": "prod_db",
  "params": {
    "dbschema_id": "my_schema",
    "object_type": "Table",
    "object_names": ["users", "orders"]
  }
}

Pipeline Run Workflow (Recommended)

For v0 pipelines, use this sequence to reduce execution-time input errors:

  1. Discover required runtime inputs
  • harness_get(resource_type="runtime_input_template", resource_id="<pipeline_id>")
  • The returned template shows <+input> placeholders that need values.
  1. Choose input strategy
  • Simple variables: pass flat key-value inputs (for example {"branch":"main","env":"prod"}).

  • Complex/structural inputs: use input_set_ids (CI codebase/build blocks and nested template inputs are best handled this way).

  • CI codebase shorthand keys (pipeline run only):

    Shorthand key Expanded structure
    branch build.type=branch, build.spec.branch=<value>
    tag build.type=tag, build.spec.tag=<value>
    pr_number build.type=PR, build.spec.number=<value>
    commit_sha build.type=commitSha, build.spec.commitSha=<value>
  • Constraint: shorthand expansion is skipped when inputs.build is already present (explicit build wins).

  1. Execute the run
  • harness_execute(resource_type="pipeline", action="run", resource_id="<pipeline_id>", ...)

  • For Git-backed pipelines whose YAML should be loaded from a non-default branch, pass params.pipeline_branch (sent to Harness as branch). This explicit definition selector takes precedence over the params.branch alias. inputs.branch independently selects the CI codebase branch:

    {
      "resource_type": "pipeline",
      "action": "run",
      "resource_id": "deploy_app",
      "params": { "pipeline_branch": "feature/new-stage" },
      "inputs": { "branch": "main" },
      "wait": true
    }
  1. Optional: combine both
  • Use input_set_ids for the base shape and inputs for simple overrides.

For v1 pipelines:

  1. Fetch harness_get(resource_type="runtime_input_template_v1", resource_id="<pipeline_id>"). For Git-backed pipelines, pass branch_name, connector_ref, and repo_name through params.
  2. Use each returned inputs[].details.name as a top-level key in harness_execute.inputs.
  3. Run harness_execute(resource_type="pipeline_v1", action="run", resource_id="<pipeline_id>", inputs={...}). The server wraps these values under an inputs: YAML root and sends the API's inputs_yaml body.

If required fields are unresolved, the tool returns a pre-flight error with expected keys and suggested input sets. You can inspect available shorthand mappings with harness_describe(resource_type="pipeline") (executeActions.run.inputShorthands).

Dynamic Pipeline Execution

Use pipeline_dynamic_execution.run when an agent or external system generates the full v0 pipeline YAML at runtime and needs to run it against an existing Harness pipeline shell. This is not a replacement for normal pipeline.run: the saved v0 pipeline must already exist, account-level and pipeline-level Allow Dynamic Execution must be enabled, and the caller needs Edit plus Execute permissions on the pipeline.

{
  "resource_type": "pipeline_dynamic_execution",
  "action": "run",
  "resource_id": "deploy_app",
  "body": {
    "yaml": "pipeline:\n  identifier: deploy_app\n  name: Deploy App\n  stages: []"
  },
  "params": {
    "module_type": "CD",
    "notes": "agent-generated dynamic run",
    "notify_only_user": true
  }
}

Constraints:

  • body must be an object with a yaml field. Raw string bodies are rejected by the public harness_execute schema.
  • body.yaml may be a YAML string or a JSON pipeline object; JSON is serialized to YAML before the request.
  • Runtime <+input> placeholders are not resolved by this API. Submit fully resolved YAML.
  • Input sets, selective stage execution, retry, and triggers are not supported by the dynamic execution endpoint.
  • The action is high_write and uses the normal confirmation/auto-approval path. The response projects the API envelope to { "execution_id": "...", "status": "..." } and includes an openInHarness execution link when scope data is available.

If Harness rejects the run as not enabled, check both the account-level Allow Dynamic Execution setting and the pipeline-level toggle under Pipeline -> Advanced Options -> Dynamic Execution Settings.

Execution Input Forensics

Use execution_inputs after a run to inspect the merged input YAML that produced a specific execution. This is useful when a failure depends on input-set merging, Git-backed input set branches, or trigger/runtime values that are hard to reconstruct from the execution page alone.

{
  "resource_type": "execution_inputs",
  "resource_id": "PLAN_EXECUTION_ID",
  "params": {
    "resolve_expressions": true,
    "resolve_expressions_type": "RESOLVE_ALL_EXPRESSIONS"
  }
}

The get response is projected to:

  • executionId - the plan execution ID from resource_id.
  • inputSetYaml - merged runtime input YAML used for the run, or null.
  • inputSetTemplateYaml - input template at execution time, or null.
  • resolvedYaml - expression-resolved YAML when resolve_expressions=true, otherwise usually null.
  • inputSetDetails - contributing saved input sets as { identifier, name } pairs.
  • inputSetBranchName - source branch for Git-backed input sets, or null.

execution_inputs is get-only and read-risk. If resolve_expressions is omitted, the server omits the API query parameters and Harness uses its default UNKNOWN resolution mode.

Pipeline Execute Wait Mode

For pipeline.run, pipeline.retry, and pipeline_v1.run, pass wait: true to let the server poll until the execution reaches a terminal status. This keeps a pipeline launch and status check in one tool call instead of asking the client or LLM to run a polling loop.

{
  "resource_type": "pipeline",
  "action": "run",
  "resource_id": "deploy_app",
  "inputs": { "branch": "main" },
  "wait": true,
  "wait_timeout_seconds": 900,
  "wait_poll_interval_seconds": 5
}

Wait mode behavior:

  • Default timeout is 600 seconds; allowed range is 10 seconds to 7200 seconds.
  • Initial poll interval defaults to 3 seconds, backs off by 1.5x, and caps at 30 seconds.
  • On success or failure, the response includes fields such as execution_id, execution_status, execution_terminal, execution_elapsed_ms, and execution_poll_count.
  • If the timeout fires, the original trigger still succeeded; the response includes execution_timed_out: true and _wait.hint with the last observed status.
  • If polling fails after the trigger succeeds, the response includes _wait.error and a recheck hint. Do not blindly rerun the pipeline unless you have confirmed the first execution is not running.
  • Failed terminal statuses include _diagnose_hint pointing to harness_diagnose(resource_type="execution", options={execution_id: "..."}).

Ask the AI DevOps Agent to create a pipeline:

{
  "prompt": "Create a pipeline that builds a Go app with Docker and deploys to Kubernetes",
  "action": "CREATE_PIPELINE"
}

Update a service via natural language:

{
  "prompt": "Add a sidecar container for logging",
  "action": "UPDATE_SERVICE",
  "conversation_id": "prev-conversation-id",
  "context": [{ "type": "yaml", "payload": "<existing service YAML>" }]
}

Pipeline Storage Modes

Harness pipelines can be stored in three ways:

Mode Description When to use
Inline Pipeline YAML stored in Harness Default. Simplest setup, no Git required.
Remote (External Git) Pipeline YAML stored in GitHub, GitLab, Bitbucket, etc. Teams using Git-backed pipeline-as-code with an external provider.
Remote (Harness Code) Pipeline YAML stored in a Harness Code repository Teams using Harness's built-in Git hosting.

Create an inline pipeline (default):

// harness_create
{
  "resource_type": "pipeline",
  "body": {
    "yamlPipeline": "pipeline:\n  name: My Pipeline\n  identifier: my_pipeline\n  stages:\n    - stage:\n        name: Build\n        type: CI\n        spec:\n          execution:\n            steps:\n              - step:\n                  type: Run\n                  name: Echo\n                  spec:\n                    command: echo hello"
  }
}

Create a remote pipeline (External Git — e.g. GitHub):

// harness_create
{
  "resource_type": "pipeline",
  "body": {
    "yamlPipeline": "pipeline:\n  name: Deploy Service\n  identifier: deploy_service\n  stages: []"
  },
  "params": {
    "store_type": "REMOTE",
    "connector_ref": "my_github_connector",
    "repo_name": "my-repo",
    "branch": "main",
    "file_path": ".harness/deploy-service.yaml",
    "commit_msg": "Add deploy pipeline via MCP"
  }
}

Create a remote pipeline (Harness Code — no connector needed):

// harness_create
{
  "resource_type": "pipeline",
  "body": {
    "yamlPipeline": "pipeline:\n  name: Build App\n  identifier: build_app\n  stages: []"
  },
  "params": {
    "store_type": "REMOTE",
    "is_harness_code_repo": true,
    "repo_name": "product-management",
    "branch": "main",
    "file_path": ".harness/build-app.yaml",
    "commit_msg": "Add build pipeline via MCP"
  }
}

Update a remote pipeline:

// harness_update
{
  "resource_type": "pipeline",
  "resource_id": "deploy_service",
  "body": {
    "yamlPipeline": "pipeline:\n  name: Deploy Service\n  identifier: deploy_service\n  stages:\n    - stage:\n        name: Deploy\n        type: Deployment"
  },
  "params": {
    "store_type": "REMOTE",
    "connector_ref": "my_github_connector",
    "repo_name": "my-repo",
    "branch": "main",
    "file_path": ".harness/deploy-service.yaml",
    "commit_msg": "Update deploy pipeline via MCP",
    "last_object_id": "abc123",
    "last_commit_id": "def456"
  }
}

Import a pipeline from an external Git repo:

// harness_execute
{
  "resource_type": "pipeline",
  "action": "import",
  "params": {
    "connector_ref": "my_github_connector",
    "repo_name": "my-repo",
    "branch": "main",
    "file_path": ".harness/existing-pipeline.yaml"
  },
  "body": {
    "pipeline_name": "Existing Pipeline",
    "pipeline_description": "Imported from GitHub"
  }
}

Import a pipeline from a Harness Code repo:

// harness_execute
{
  "resource_type": "pipeline",
  "action": "import",
  "params": {
    "is_harness_code_repo": true,
    "repo_name": "product-management",
    "branch": "main",
    "file_path": ".harness/existing-pipeline.yaml"
  },
  "body": {
    "pipeline_name": "Existing Pipeline"
  }
}

Create a connector:

{
  "resource_type": "connector",
  "body": { "connector": { "name": "My Docker Hub", "identifier": "my_docker", "type": "DockerRegistry" } }
}

Delete a trigger:

{
  "resource_type": "trigger",
  "resource_id": "nightly-trigger",
  "pipeline_id": "my-pipeline"
}

List input sets for a pipeline:

{
  "resource_type": "input_set",
  "pipeline_id": "my-pipeline"
}

Get a specific input set:

{
  "resource_type": "input_set",
  "resource_id": "prod-inputs",
  "pipeline_id": "my-pipeline"
}

Create an input set:

{
  "resource_type": "input_set",
  "pipeline_id": "my-pipeline",
  "body": "inputSet:\n  name: Production Inputs\n  identifier: prod_inputs\n  pipeline:\n    identifier: my-pipeline\n    variables:\n      - name: env\n        type: String\n        value: production"
}

Update an input set:

{
  "resource_type": "input_set",
  "resource_id": "prod_inputs",
  "pipeline_id": "my-pipeline",
  "body": "inputSet:\n  name: Production Inputs\n  identifier: prod_inputs\n  pipeline:\n    identifier: my-pipeline\n    variables:\n      - name: env\n        type: String\n        value: production\n      - name: replicas\n        type: String\n        value: \"3\""
}

Delete an input set:

{
  "resource_type": "input_set",
  "resource_id": "prod_inputs",
  "pipeline_id": "my-pipeline"
}

Resource Types

261 resource types organized across 42 toolsets. Each resource type supports a subset of CRUD operations and optional execute actions.

Platform

Resource Type List Get Create Update Delete Execute Actions
organization x x x x x
project x x x x x

Pipelines

Resource Type List Get Create Update Delete Execute Actions
pipeline x x x x x run, retry
pipeline_v1 (Alpha) x x x x x run
pipeline_dynamic_execution run
execution x x interrupt
execution_inputs x
trigger x x x x x
pipeline_summary x
input_set x x x x x
runtime_input_template x
runtime_input_template_v1 x
pipeline_resolved_yaml x
approval_instance x approve, reject

Both pipeline YAML resource types are available when the pipelines toolset is enabled. HARNESS_PIPELINE_VERSION and the HTTP x-harness-pipeline-version initialization header select the default version preference; they do not hide the other version.

AI Agents

Resource Type List Get Create Update Delete Execute Actions
agent x x x x x
agent_run x

Services

Resource Type List Get Create Update Delete Execute Actions
service x x x x x

Environments

Resource Type List Get Create Update Delete Execute Actions
environment x x x x x move_configs

Connectors

Resource Type List Get Create Update Delete Execute Actions
connector x x x x x test_connection
connector_catalogue x
llm_model x

Infrastructure

Resource Type List Get Create Update Delete Execute Actions
infrastructure x x x x x move_configs

Secrets

Resource Type List Get Create Update Delete Execute Actions
secret x x

Execution Logs

Resource Type List Get Create Update Delete Execute Actions
execution_log x

Audit Trail

Resource Type List Get Create Update Delete Execute Actions
audit_event x x

Delegates

Resource Type List Get Create Update Delete Execute Actions
delegate x x
delegate_token x x x x revoke, get_delegates

Code Repositories

Resource Type List Get Create Update Delete Execute Actions
repository x x x x
branch x x x x
commit x x x diff, diff_stats
file_content x x blame
tag x x x
repo_rule x x
space_rule x x

commit creation commits one or more file actions directly through the Harness Code API without cloning. Pass body.title, body.branch, and body.actions; each action is CREATE, UPDATE, DELETE, or MOVE, and UPDATE requires the current blob SHA.

file_content list returns every path at a ref; get returns file or directory content (omit or pass empty path for the repo root; nested paths keep slashes). Omit git_ref to use the repository default branch — do not guess main.

Artifact Registries

Resource Type List Get Create Update Delete Execute Actions
registry x x
artifact x
artifact_version x
artifact_file x

File Store

Resource Type List Get Create Update Delete Execute Actions
file_store x x x x x list_children

file_store manages Harness File Store files and folders through the generic tools. It supports account, org, and project scope; pass resource_scope="account"|"org"|"project" or paste a Harness File Store URL so the server can derive scope and IDs.

Common calls:

# List the account-level File Store.
harness_list(resource_type="file_store", resource_scope="account")

# Create a folder at the current scope root.
harness_create(resource_type="file_store", body={
  name: "scripts",
  type: "FOLDER",
  parent_identifier: "Root"
})

# Upload a UTF-8 script file. Use content_base64 instead for binary data.
harness_create(resource_type="file_store", body={
  name: "deploy.sh",
  type: "FILE",
  parent_identifier: "Root",
  content: "#!/usr/bin/env bash\n./deploy",
  mime_type: "text/x-shellscript",
  file_usage: "SCRIPT"
})

# Rename metadata without replacing file content.
harness_update(resource_type="file_store", resource_id="deploy_script", body={
  name: "deploy-prod.sh",
  type: "FILE",
  parent_identifier: "Root"
})

# List first-level children of a folder. This is a read-risk execute action.
harness_execute(resource_type="file_store", action="list_children",
  resource_id="scripts_folder", params={folder_name: "scripts"})

Multipart body constraints:

  • Create/update accept JSON body, then convert it to multipart/form-data for /ng/api/file-store.
  • name, type (FILE or FOLDER), and parent_identifier are required; use the literal "Root" only for the root of the selected scope.
  • FILE create requires exactly one of content (UTF-8 string) or content_base64 (valid non-empty base64). FILE update can omit content for metadata-only updates, or provide exactly one content field to replace content.
  • FOLDER create/update must omit content and content_base64.
  • Optional file_usage must be MANIFEST_FILE, CONFIG, or SCRIPT; optional scalar metadata such as description, mime_type, path, and tags must be strings.
  • Upload content is capped at 100 MB. Confirmation prompts redact content, content_base64, and contentBase64 previews before elicitation.

list_children accepts either shorthand (resource_id plus params.folder_name, or params.file_store_id/params.folder_identifier plus params.folder_name) or a full FileStoreNode body with identifier, name, and type: "FOLDER". Full bodies use Harness camelCase parentIdentifier; shorthand may use params.parent_identifier.

Templates

Resource Type List Get Create Update Delete Execute Actions
template x x x x x

Template operations use the Harness Template service paths (/template/api/templates...). Create and update require the full template YAML string in body.template_yaml or body.yaml; version_label targets a specific version for update/delete, while deleting without version_label deletes all versions.

Dashboards

Resource Type List Get Create Update Delete Execute Actions
dashboard x x
dashboard_data x

Database DevOps

Resource Type List Get Create Update Delete Execute Actions
database_schema x x x x x
database_instance x x x x x
database_snapshot_object x x
database_llm_authoring_pipeline x

Infrastructure as Code Management (IaCM)

IaCM resources are default-enabled and mostly project-scoped. Start with iacm_workspace to find workspace identifiers, then use that workspace_id for workspace resources, costs, and activity diffs. Use iacm_variable_set for reusable variable sets at account, org, or project scope. The provider registry is account-scoped.

iacm_module spans account, org, and project scope. It defaults to the account registry; every operation (list, get, create, update) sends the same scope_org / scope_project query params, so a module you create is discoverable at the scope you created it. Select the scope with resource_scope="account" | "org" | "project" plus org_id/project_id. Scoping is opt-in: when resource_scope is omitted, org_id/project_id apply only if you pass them explicitly — configured HARNESS_ORG/HARNESS_PROJECT defaults are not applied, so an ambient project config cannot silently register an account module under a project. A module body's own org/project fields locate its Git connector and are unrelated to this visibility scope.

iacm_workspace create/update return { policy_evaluation } only — follow up with harness_get to fetch the workspace. iacm_variable_set and iacm_module create/update return the resource itself. iacm_provider create returns { id } only — follow up with harness_get; update is version-oriented only (POST/PUT /providers/{id}/version) — there is no metadata PUT. Version writes may return an empty body; HarnessClient normalizes that to { status: "SUCCESS", message: "No content" }.

Variable-set update is HTTP PUT with full-replacement collections — always harness_get first, then PUT the full desired body (terraform_variables / environment_variables are required on update; omit/empty clears connectors and variable files). Module update is also PUT — prefer get-then-put for optional fields. Writes are medium_write and require confirmation (elicitation or confirm: true).

Variable-set and provider-registry RBAC (iac_variableset_*, iac_providerregistry_*) are currently Experimental in Harness — access checks always allow until iac-server activates enforcement. Module registry RBAC (iac_registry_view / iac_registry_edit) is Active and enforceable. MCP always forwards the caller PAT/SAT unchanged.

Resource Type List Get Create Update Delete Execute Actions
iacm_workspace x x x x
iacm_variable_set x x x x
iacm_resource x
iacm_module x x x x
iacm_provider x x x x
iacm_workspace_costs x
iacm_activity_resource_change x

Typical workflow:

  1. harness_list(resource_type="iacm_workspace", org_id="...", project_id="...") to find the workspace.
  2. harness_create / harness_update on iacm_workspace to create from scratch or a template (associated_template), or update an existing workspace — response is { policy_evaluation } only.
  3. harness_get(resource_type="iacm_workspace", workspace_id="...") to fetch the created/updated workspace.
  4. harness_list / harness_create / harness_update on iacm_variable_set (optionally with resource_scope) for reusable Terraform/env variable sets — response is the VariableSet resource.
  5. harness_list / harness_create / harness_update on iacm_module for the module registry (name + system required; add resource_scope with org_id/project_id for an org- or project-scoped module) — response is the module resource.
  6. harness_list / harness_create / harness_update on iacm_provider for the account provider registry (body.type required for create; create returns { id } only — then harness_get; update creates/updates versions only) — version update may return empty success.
  7. harness_list(resource_type="iacm_resource", org_id="...", project_id="...", workspace_id="...") to inspect Terraform resources, outputs, and data sources.
  8. harness_list(resource_type="iacm_workspace_costs", org_id="...", project_id="...", workspace_id="...") to review per-execution cost entries.
  9. harness_list(resource_type="iacm_activity_resource_change", org_id="...", project_id="...", activity_id="...", workspace_id="...") to inspect before/after resource diffs for a plan, apply, or destroy activity.

IaCM list responses expose page_count as the count for the current page only (except iacm_variable_set, which is not paginated). When has_more is true, keep requesting the next 1-based page and sum page counts if you need a total.

Internal Developer Portal (IDP)

Resource Type List Get Create Update Delete Execute Actions
idp_entity x x
scorecard x x
scorecard_check x x
scorecard_stats x
scorecard_check_stats x
idp_score x x
idp_workflow x execute
idp_tech_doc x

Pull Requests

Resource Type List Get Create Update Delete Execute Actions
pull_request x x x x close, merge
pr_reviewer x x submit_review
pr_comment x x x resolve, unresolve
pr_check x
pr_activity x

Use harness_execute(resource_type="pull_request", action="close", ...) for an explicit close operation. harness_update also accepts body.state (open or closed) and routes state changes to the dedicated Harness Code PR state endpoint; send title/description edits in a separate update call.

Use harness_list(resource_type="pr_activity", filters={type: ["comment", "code-comment"]}, ...) to read PR comments. Use pr_comment for comment write operations.

Use harness_execute(resource_type="pr_comment", action="resolve", ...) to resolve a comment thread and action="unresolve" to reopen it; neither takes a body. comment_id must be a row's id from pr_activity where that row's parent_id is null; the API rejects reply ids.

Release Management

Release Management (RMG) resources are default-enabled. Definition resources (release_process, release_activity) support list/get/create/update/delete with body.yaml; call harness_schema(resource_type="release_process"|"release_activity") before create/update. Execution resources monitor running releases — most list operations require release_id (UUID from harness_list resource_type=release, or the UI URL slug such as identifier-1.0.0-abc). Paste an RMG release URL into harness_list to auto-fill release_id.

RMG calls use ${HARNESS_BASE_URL}/gateway/rmg with account scoping via the Harness-Account header. Org/project scope uses header-based scoping when org_id/project_id are provided. release_execution_phase is list-only — use each phase item's identifier field as params.phase_identifier when calling harness_get on phase input/output resources (do not call harness_get on release_execution_phase itself). Release list status filtering is applied client-side on the current page only; keep paging with the same filters when results may span pages.

Resource Type List Get Create Update Delete Execute Actions
release_process x x x x x
release_activity x x x x x
release x x
release_execution_phase x
release_execution_task x
release_execution_activity x
release_input x
release_execution_phase_input x
release_execution_phase_output x
release_execution_activity_input x
release_execution_activity_output x

Typical workflow:

  1. harness_list(resource_type="release_process", org_id="...", project_id="...") to discover orchestration process definitions.
  2. harness_schema(resource_type="release_process") (or release_activity) before create/update; then harness_create / harness_update with body.yaml.
  3. harness_list(resource_type="release", org_id="...", project_id="...") to find active or recent releases (default 30-day look-back; optional filters.status, filters.search_term, filters.days_back).
  4. harness_get(resource_type="release", release_id="...") for release details.
  5. harness_list(resource_type="release_execution_phase", filters={ release_id: "..." }) for phase status; same release_id for release_execution_task and release_execution_activity.
  6. harness_get on release_input, release_execution_phase_input, release_execution_phase_output, release_execution_activity_output, or release_execution_activity_input using release_id plus params.phase_identifier / params.activity_identifier / activity_execution_id as documented on each resource.

Vibe

The default-enabled vibe toolset covers the Vibe Orchestrator BFF contract under ${HARNESS_BASE_URL}/vibe/v1. It uses the existing Harness connection and account header, without adding account/org/project query parameters or scope fields to request bodies. The team validated the Vibe flow using Harness API-key authentication (PAT/SAT), so no opt-in setting is required for default sessions. The curated OpenAPI documents bearer/session authentication; the server's OAuth mode forwards the current session's bearer token. Automated regressions verify both header paths; gateway authentication remains subject to the target environment's configuration.

Resource Type List Get Create Update Delete Execute Actions
vibe_project x prepare, deploy
vibe_app_lifecycle x events

The API supports two intake paths. Keep these API-native request shapes:

Source available to the coding agent API flow
GitHub repository link/connector harness_create with resource_type="vibe_project" and body.mode plus the mode-specific fields. The contract names github_link and github_connector but does not define their URL, branch, or connector field shapes; these fields are forwarded to the backend without inventing a mapping.
ZIP file Call prepare with the app name and file metadata, upload the bytes to the returned signed target, then call deploy.
Local source directory The coding agent archives the intended workspace source into a ZIP locally, then follows the ZIP flow. A local path or conversational context is not an API-supported source upload.

When packaging a directory, include the source, manifests, lockfiles, configuration, and intended uncommitted edits needed to build it. Exclude credentials, .git, installed dependencies, and generated artifacts. Packaging and the signed upload happen where the files are accessible; a hosted MCP server cannot read the coding agent's local directory.

For an existing ZIP, prepare the upload:

{
  "resource_type": "vibe_project",
  "action": "prepare",
  "body": {
    "name": "demo-app",
    "file": {
      "path": "app.zip",
      "size_bytes": 12345,
      "content_type": "application/zip"
    }
  }
}

Pass this to harness_execute. The size must describe the actual ZIP; size_bytes, content_type, and md5 are optional and nullable. Additional prepare fields are preserved for backend validation, as permitted by the OpenAPI. Preparation returns projectId, sourceId, and upload, including each file's uploadUrl, method, headers, and expiresAt. Upload the file bytes directly using that signed URL, method, and headers; preserve the URL exactly and do not add Harness credentials to the storage request. The prepare action does not read or upload local files.

After a successful upload, explicitly deploy:

{
  "resource_type": "vibe_project",
  "action": "deploy",
  "resource_id": "<projectId returned by prepare>"
}

Facts

Kind
MCP server
Repo
harness/mcp-server
Group
Uncategorized
Stars
105
License
MIT
Language
TypeScript
Last push
2026-10-11
Forks
89
Topics
harness, mcp-server, mcp

More on this shelf

  1. 1REAio.github.morluto/reaReverse engineer anything from your terminal or agent with one CLI and MCP server.125.3k
  2. 2com.browser-use/browser-usecom.browser-use/browser-useControl a real Chrome browser to complete any task: fill forms, extract data, book flights.117.7k
  3. 3World Monitorapp.worldmonitor/mcpLive markets, conflicts, country risk, chokepoints, energy, and China decision signals. 93 tools.88.3k
  4. 4Scrapling MCP Serverd4vinci/scrapling🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scraplingdev86.9k
  5. 5Everythingmodelcontextprotocol/serversThis MCP server attempts to exercise all the features of the MCP protocol. It is not intended to be a useful server, but rather a test server for builders of MCP clients. It implements prompts, tools, resources, sampling, and more to showcase MCP capabilities.85.8k
  6. 6Fetchmodelcontextprotocol/serversA Model Context Protocol server that provides web content fetching capabilities. This server enables LLMs to retrieve and process content from web pages, converting HTML to markdown for easier consumption.85.8k