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 263 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, 263 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:
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. - OAuth proxy mode accepts an upstream identity-provider token, exchanges it internally for a per-user HarnessID token, and requires the upstream token on every request.
- 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.
Upstream Identity Provider OAuth Proxy Mode
OAuth proxy mode needs a confidential HarnessID client. The Harness ID team creates that client and provides the client ID and client secret. Reach out to the Harness ID team before enabling this mode.
Set HARNESS_MCP_MODE=oauth-proxy when an MCP client must hold only an upstream identity-provider token. The public client name can be anything. Set HARNESS_MCP_BROKER_CLIENT_ID to the same name the MCP client uses. The integrated OAuth broker starts the upstream browser login, completes the HarnessID login in the same flow, and returns only the upstream access and refresh tokens to the MCP client. On every /mcp request, the server validates the upstream token and uses a cached per-user HarnessID access token, renewing it with the refresh token before expiry. A missing upstream-to-HarnessID mapping returns 401 so the client signs in again. HarnessID tokens stay on the server. Refresh tokens, cached access tokens, and transient OAuth state are encrypted at rest.
This mode is HTTP-only. It also needs the upstream issuer and audience, and a stable 32-byte vault key. See Upstream identity provider OAuth proxy mode for configuration and end-to-end MCP test 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.
Semantic Search
harness_search uses semantic routing to narrow scatter-gather API calls before fanning out to Harness. Three search providers are available:
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.
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):
-
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:
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
263 resource types organized across 42 toolsets. Each resource type supports a subset of CRUD operations and optional execute actions.
Platform
Pipelines
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
Services
Environments
Connectors
Infrastructure
Secrets
Execution Logs
Audit Trail
Delegates
Code Repositories
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
File Store
file_store manages Harness File Store files and folders through the generic tools. It supports account, org, and project scope; pass `resource_scope="ac