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 258 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, 258 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. 41 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
. . . - 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.or... sat.), so... HARNESS_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/, and the 512×512 bundle icon is tracked at 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 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": |
| /.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. - 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 , 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_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 endpointhttps://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"
}
}
}
}
Troubleshootingnpx ENOENTornode: 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 changingHARNESS_API_KEYwill not affectspawn npx ENOENT.
> GUI apps (Cursor, Claude Desktop, Devin Desktop, VS Code) don't always inherit your shell'sPATH, 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 withwhich 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 | | 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.
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.
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": , 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.
... (README truncated for length)