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.
Files
Harness MCP Server 2.0
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:
- Log in to your Harness account
- Go to My Profile → API Keys → + New API Key
- Create a new Token under the API key — this generates a PAT or SAT in the format
<prefix>.<accountId>.<tokenId>.<secret> - 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>...orsat.<accountId>...), soHARNESS_ACCOUNT_IDis 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_TOKENfor shared or remotely reachable single-user and multi-user deployments. When set, everyPOST,GET, andDELETErequest to/mcpmust includeAuthorization: Bearer <token>. - OAuth mode accepts HarnessID access tokens instead of
HARNESS_MCP_AUTH_TOKENand can bind to a non-loopback address without the unauthenticated opt-out. - Non-loopback single-user and multi-user binds require
HARNESS_MCP_AUTH_TOKENby default. To run unauthenticated on a non-loopback interface anyway, setHARNESS_MCP_ALLOW_UNAUTHENTICATED_HTTP=trueexplicitly. POST /mcpwithoutmcp-session-idmust be aninitializerequest.POST /mcp,GET /mcp, andDELETE /mcpfor existing sessions require themcp-session-idheader.GET /mcpis used for SSE notifications (progress updates and elicitation prompts).- Idle sessions are reaped after
MCP_SESSION_TTL_MSmilliseconds once no request or SSE stream is active (default1800000, or 30 minutes). GET /healthis the only non-MCP endpoint.- Request body size is capped by
HARNESS_MAX_BODY_SIZE_MB(default10MB). - Set
x-harness-pipeline-version: 0or1on theinitializerequest to select V0 or V1 pipeline resources for that HTTP session. - Set
x-harness-auto-approve-risk: none|low_write|medium_write|high_write|allon theinitializerequest to choose a stricter per-session auto-approval threshold. The server caps this value at the deployment-levelHARNESS_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_KEYmust not be set in the server config — the server holds no Harness credentials.- Each session must provide
x-harness-api-keyon theinitializerequest.x-harness-account-idis required only when the API key does not embed an account segment. - Sessions may also provide
x-harness-organdx-harness-projectheaders 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_TOKENis 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_ORGandHARNESS_PROJECTare optional. They set the org ID and project ID used when not specified per tool call. Agents can discover orgs and projects dynamically usingharness_list(resource_type="organization")andharness_list(resource_type="project"). The deprecated namesHARNESS_DEFAULT_ORG_IDandHARNESS_DEFAULT_PROJECT_IDare 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_KEYin 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/mcpis 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 setHARNESS_BASE_URLto 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 ENOENTornode: No such file or directoryThis is a client process-launch failure, not a Harness authentication failure. The MCP server has not started yet, so changing
HARNESS_API_KEYwill not affectspawn npx ENOENT.GUI apps (Cursor, Claude Desktop, Devin Desktop, VS Code) don't always inherit your shell's
PATH, so they can fail to findnpxornodeafter a config reload. Fix this by using absolute paths and explicitly settingPATHin theenvblock:{ "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 npxandwhich nodein a terminal, then make sure the directory containingnodeis included in thePATHvalue above. Common locations:
- Homebrew (macOS):
/opt/homebrew/bin/npx- nvm:
~/.nvm/versions/node/v20.x.x/bin/npx(runnvm which currentto 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_FILEappends newline-delimited JSON events for local collection.HARNESS_AUDIT_WEBHOOK_URLposts{ "events": [...] }batches to an HTTPS webhook, optionally withHARNESS_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_ENDPOINTenables 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 onlyaccountIdentifier.resource_scope: "org"sendsaccountIdentifierandorgIdentifier.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, andagent-pipeline. - Entity schemas include
connector,environment,service,secret, andinfrastructure. They are scope-aware (account,org, orproject) and requireorg_id/project_idwhen the selected scope requires them. - Release Management definitions (
release_process,release_activity) fetch live JSON Schema from RMG/api/yamlSchema(not bundled). Passscope,org_id, andproject_idwhen 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-schemaAPI and caches the result. - Omit
pathfor a field/section summary, then pass a dot-separatedpathto 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:
- Discover required runtime inputs
harness_get(resource_type="runtime_input_template", resource_id="<pipeline_id>")- The returned template shows
<+input>placeholders that need values.
- 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 branchbuild.type=branch,build.spec.branch=<value>tagbuild.type=tag,build.spec.tag=<value>pr_numberbuild.type=PR,build.spec.number=<value>commit_shabuild.type=commitSha,build.spec.commitSha=<value> -
Constraint: shorthand expansion is skipped when
inputs.buildis already present (explicitbuildwins).
- 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 asbranch). This explicit definition selector takes precedence over theparams.branchalias.inputs.branchindependently 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 }
- Optional: combine both
- Use
input_set_idsfor the base shape andinputsfor simple overrides.
For v1 pipelines:
- Fetch
harness_get(resource_type="runtime_input_template_v1", resource_id="<pipeline_id>"). For Git-backed pipelines, passbranch_name,connector_ref, andrepo_namethroughparams. - Use each returned
inputs[].details.nameas a top-level key inharness_execute.inputs. - Run
harness_execute(resource_type="pipeline_v1", action="run", resource_id="<pipeline_id>", inputs={...}). The server wraps these values under aninputs:YAML root and sends the API'sinputs_yamlbody.
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:
bodymust be an object with ayamlfield. Raw string bodies are rejected by the publicharness_executeschema.body.yamlmay 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_writeand uses the normal confirmation/auto-approval path. The response projects the API envelope to{ "execution_id": "...", "status": "..." }and includes anopenInHarnessexecution 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 fromresource_id.inputSetYaml- merged runtime input YAML used for the run, ornull.inputSetTemplateYaml- input template at execution time, ornull.resolvedYaml- expression-resolved YAML whenresolve_expressions=true, otherwise usuallynull.inputSetDetails- contributing saved input sets as{ identifier, name }pairs.inputSetBranchName- source branch for Git-backed input sets, ornull.
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, andexecution_poll_count. - If the timeout fires, the original trigger still succeeded; the response includes
execution_timed_out: trueand_wait.hintwith the last observed status. - If polling fails after the trigger succeeds, the response includes
_wait.errorand a recheck hint. Do not blindly rerun the pipeline unless you have confirmed the first execution is not running. - Failed terminal statuses include
_diagnose_hintpointing toharness_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 tomultipart/form-datafor/ng/api/file-store. name,type(FILEorFOLDER), andparent_identifierare required; use the literal"Root"only for the root of the selected scope.FILEcreate requires exactly one ofcontent(UTF-8 string) orcontent_base64(valid non-empty base64).FILEupdate can omit content for metadata-only updates, or provide exactly one content field to replace content.FOLDERcreate/update must omitcontentandcontent_base64.- Optional
file_usagemust beMANIFEST_FILE,CONFIG, orSCRIPT; optional scalar metadata such asdescription,mime_type,path, andtagsmust be strings. - Upload content is capped at 100 MB. Confirmation prompts redact
content,content_base64, andcontentBase64previews 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:
harness_list(resource_type="iacm_workspace", org_id="...", project_id="...")to find the workspace.harness_create/harness_updateoniacm_workspaceto create from scratch or a template (associated_template), or update an existing workspace — response is{ policy_evaluation }only.harness_get(resource_type="iacm_workspace", workspace_id="...")to fetch the created/updated workspace.harness_list/harness_create/harness_updateoniacm_variable_set(optionally withresource_scope) for reusable Terraform/env variable sets — response is the VariableSet resource.harness_list/harness_create/harness_updateoniacm_modulefor the module registry (name+systemrequired; addresource_scopewithorg_id/project_idfor an org- or project-scoped module) — response is the module resource.harness_list/harness_create/harness_updateoniacm_providerfor the account provider registry (body.typerequired for create; create returns{ id }only — thenharness_get; update creates/updates versions only) — version update may return empty success.harness_list(resource_type="iacm_resource", org_id="...", project_id="...", workspace_id="...")to inspect Terraform resources, outputs, and data sources.harness_list(resource_type="iacm_workspace_costs", org_id="...", project_id="...", workspace_id="...")to review per-execution cost entries.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:
harness_list(resource_type="release_process", org_id="...", project_id="...")to discover orchestration process definitions.harness_schema(resource_type="release_process")(orrelease_activity) before create/update; thenharness_create/harness_updatewithbody.yaml.harness_list(resource_type="release", org_id="...", project_id="...")to find active or recent releases (default 30-day look-back; optionalfilters.status,filters.search_term,filters.days_back).harness_get(resource_type="release", release_id="...")for release details.harness_list(resource_type="release_execution_phase", filters={ release_id: "..." })for phase status; samerelease_idforrelease_execution_taskandrelease_execution_activity.harness_getonrelease_input,release_execution_phase_input,release_execution_phase_output,release_execution_activity_output, orrelease_execution_activity_inputusingrelease_idplusparams.phase_identifier/params.activity_identifier/activity_execution_idas 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
- Group
- Uncategorized
- Stars
- 105
- License
- MIT
- Language
- TypeScript
- Last push
- 2026-10-11
- Forks
- 89
- Topics
- harness, mcp-server, mcp
- 1REAio.github.morluto/reaReverse engineer anything from your terminal or agent with one CLI and MCP server.125.3k
- 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
- 3World Monitorapp.worldmonitor/mcpLive markets, conflicts, country risk, chokepoints, energy, and China decision signals. 93 tools.88.3k
- 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
- 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
- 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