harness-mcp
AI-native interface to the Harness Platform — ask questions, diagnose failures, and manage deployments without leaving your terminal
Open source Open in the app JSON README (API)
About
AI-native interface to the Harness Platform — ask questions, diagnose failures, and manage deployments without leaving your terminal
Details
- Kind
- Plugins
- Topic
- Developer tools
- Publisher
- harness
- Origin
- gemini
- Category
- ferramentas
- Version
- 0.5.2
- Stars
- 96
- Forks
- 79
- Open pull requests
- 48
- Last push
- 2026-09-07T14:47:19Z
- Repository state
- ativo
- Language
- TypeScript
- License
- MIT
- Added
- 2026-08-30 14:13:39
- Updated
- 2026-08-30 14:13:39
- Origin id
harness/mcp-server
README
## Harness MCP Server 2.0
[](https://mcptoplist.com/server/glama%2Fharness%2Fmcp-server)
An MCP (Model Context Protocol) server that gives AI agents full access to the Harness.io platform through 11 consolidated tools and 243 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, 243 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.** 40 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 coverage is available when you need inventory and playbook data.
- **Multi-project workflows out of the box.** Agents discover organizations and projects dynamically — no hardcoded env vars needed. Ask "show failed executions across all projects" and the agent can navigate the full account hierarchy.
- **35 prompt templates.** Pre-built prompts for common workflows: build & deploy apps end-to-end, debug failed pipelines, review DORA metrics, triage vulnerabilities, optimize cloud costs, audit access control, plan feature flag rollouts, review pull requests, approve pending pipelines, and more.
- **Works everywhere.** Stdio transport for local clients (Claude Desktop, Cursor, Devin Desktop), HTTP transport for remote/shared deployments, Docker and Kubernetes ready.
- **Zero-config start.** Just provide a Harness API key. Account ID is auto-extracted from PAT and SAT tokens, org/project defaults are optional, and toolset filtering lets you expose only what you need.
- **Extensible by design.** Adding a new Harness resource means adding a declarative data file — no new tool registration, no schema changes, no prompt updates.
## Prerequisites
Before installing or running the server, you need a Harness API key:
1. Log in to your [Harness account](https://app.harness.io)
2. Go to **My Profile** → **API Keys** → **+ New API Key**
3. Create a new **Token** under the API key — this generates a PAT or SAT in the format `<prefix>.<accountId>.<tokenId>.<secret>`
4. Save the token somewhere secure — you'll need it in the next step
> For detailed instructions, see the [Harness API Quickstart](https://developer.harness.io/docs/platform/automation/api/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](#hosted-harness-mcp) for configuration examples.
### Option 1: npx (Recommended)
No install required — just run it:
```bash
HARNESS_API_KEY=pat.xxx.xxx.xxx npx harness-mcp-v2@latest
```
Or configure the API key in your AI client (see [Client Configuration](#client-configuration) below).
```bash
# Stdio transport (default — for Claude Desktop, Cursor, Devin Desktop, etc.)
HARNESS_API_KEY=pat.xxx npx harness-mcp-v2
# HTTP transport (for remote/shared deployments)
HARNESS_API_KEY=pat.xxx npx harness-mcp-v2 http --port 8080
```
> **Note:** The account ID is auto-extracted from PAT and SAT tokens (`pat.<accountId>...` or `sat.<accountId>...`), so `HARNESS_ACCOUNT_ID` is only needed for API keys without an embedded account segment.
### Option 2: Global Install
```bash
npm install -g harness-mcp-v2
# Then run directly
harness-mcp-v2
```
### Option 3: Build from Source
For development or customization:
```bash
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:
```bash
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
```bash
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> }` |
The HTTP transport runs in **session-based mode**. A new MCP session is created on `initialize`, the server returns an `mcp-session-id` header, and subsequent requests for that session must include the same header.
Operational constraints in HTTP mode:
- Set `HARNESS_MCP_AUTH_TOKEN` for any shared or remotely reachable deployment. When set, every `POST`, `GET`, and `DELETE` request to `/mcp` must include `Authorization: Bearer <token>`.
- Non-loopback binds require `HARNESS_MCP_AUTH_TOKEN` by default. To run unauthenticated on a non-loopback interface anyway, set `HARNESS_MCP_ALLOW_UNAUTHENTICATED_HTTP=true` explicitly.
- `POST /mcp` without `mcp-session-id` must be an `initialize` request.
- `POST /mcp`, `GET /mcp`, and `DELETE /mcp` for existing sessions require the `mcp-session-id` header.
- `GET /mcp` is used for SSE notifications (progress updates and elicitation prompts).
- Idle sessions are reaped after `MCP_SESSION_TTL_MS` milliseconds once no request or SSE stream is active (default `1800000`, or 30 minutes).
- `GET /health` is the only non-MCP endpoint.
- Request body size is capped by `HARNESS_MAX_BODY_SIZE_MB` (default `10` MB).
- Set `x-harness-pipeline-version: 0` or `1` on the `initialize` request to select V0 or V1 pipeline resources for that HTTP session.
- Set `x-harness-auto-approve-risk: none|low_write|medium_write|high_write|all` on the `initialize` request to choose a stricter per-session auto-approval threshold. The server caps this value at the deployment-level `HARNESS_AUTO_APPROVE_RISK`, so a session can reduce but not expand the configured approval ceiling.
#### Multi-User Mode
Set `HARNESS_MCP_MODE=multi-user` for shared HTTP deployments where each client authenticates as a different Harness user. In this mode:
- `HARNESS_API_KEY` must **not** be set in the server config — the server holds no Harness credentials.
- Each session must provide `x-harness-api-key` on the `initialize` request. `x-harness-account-id` is required only when the API key does not embed an account segment.
- Sessions may also provide `x-harness-org` and `x-harness-project` headers to set default scope for that session.
- The Harness API key flows through to every Harness API call for that session, so the audit trail in Harness reflects the real user.
- `HARNESS_MCP_AUTH_TOKEN` is independent and can still be used as an additional transport-layer gate.
```bash
# Health check
curl http://localhost:3000/health
# MCP initialize request (capture mcp-session-id response header)
# In multi-user mode, x-harness-api-key is required on initialize.
# x-harness-account-id is needed only for API keys without an embedded account segment.
curl -i -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer $HARNESS_MCP_AUTH_TOKEN" \
-H "x-harness-api-key: $HARNESS_API_KEY" \
-H "x-harness-account-id: $HARNESS_ACCOUNT_ID" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# Subsequent MCP request (use returned session ID)
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer $HARNESS_MCP_AUTH_TOKEN" \
-H "mcp-session-id: <session-id>" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# Terminate session
curl -X DELETE http://localhost:3000/mcp \
-H "Authorization: Bearer $HARNESS_MCP_AUTH_TOKEN" \
-H "mcp-session-id: <session-id>"
```
`HARNESS_MCP_ALLOWED_HOSTS` controls Host-header validation for DNS-rebinding protection, and CORS limits browser origins. Neither is authentication; use `HARNESS_MCP_AUTH_TOKEN` or an authenticated gateway/reverse proxy for access control.
### Client Configuration
> **Note:** `HARNESS_ORG` and `HARNESS_PROJECT` are optional. They set the org ID and project ID used when not specified per tool call. Agents can discover orgs and projects dynamically using `harness_list(resource_type="organization")` and `harness_list(resource_type="project")`. The deprecated names `HARNESS_DEFAULT_ORG_ID` and `HARNESS_DEFAULT_PROJECT_ID` are still accepted for backward compatibility.
#### Hosted Harness MCP
Harness also supports a hosted MCP endpoint for accounts that have the managed service enabled. This is useful when you want a shared remote MCP endpoint instead of running `npx harness-mcp-v2` or self-hosting the HTTP transport yourself.
> **Important:** Hosted MCP authentication uses **Harness Platform OAuth**. It does **not** use `HARNESS_API_KEY` in the client config. Hosted MCP availability is configured per Harness account, so you will need to work with **Harness Support** to enable/configure the setting before using it.
>
> The hosted endpoint `https://mcp.harness.io/mcp` is a managed service. Client-side MCP config in Claude, Cursor, or Cowork cannot override which Harness environment it routes to. For Harness0 or another private Harness SaaS environment, ask Harness Support to enable/configure hosted MCP for that environment, or run the local/self-hosted server and set `HARNESS_BASE_URL` to the target Harness host.
**Hosted MCP example:**
```json
{
"mcpServers": {
"harness-prod1-mcp": {
"url": "https://mcp.harness.io/mcp",
"auth": {
"CLIENT_ID": "mcp-client"
}
}
}
}
```
**Example with both hosted and local entries:**
```json
{
"mcpServers": {
"harness-hosted": {
"url": "https://mcp.harness.io/mcp",
"auth": {
"CLIENT_ID": "mcp-client"
}
},
"harness-local": {
"command": "/absolute/path/to/npx",
"args": ["-y", "harness-mcp-v2@latest"],
"env": {
"HARNESS_API_KEY": "pat.xxx.xxx.xxx",
"PATH": "/directory/containing/node:/usr/local/bin:/usr/bin:/bin"
}
}
}
}
```
> **Troubleshooting `npx ENOENT` or `node: No such file or directory`**
>
> This is a client process-launch failure, not a Harness authentication failure. The MCP server has not started yet, so changing `HARNESS_API_KEY` will not affect `spawn npx ENOENT`.
>
> GUI apps (Cursor, Claude Desktop, Devin Desktop, VS Code) don't always inherit your shell's `PATH`, so they can fail to find `npx` or `node` after a config reload. Fix this by using absolute paths and explicitly setting `PATH` in the `env` block:
>
> ```json
> {
> "mcpServers": {
> "harness": {
> "command": "/absolute/path/to/npx",
> "args": ["-y", "harness-mcp-v2"],
> "env": {
> "HARNESS_API_KEY": "pat.xxx.xxx.xxx",
> "PATH": "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
> }
> }
> }
> }
> ```
>
> Find your paths with `which npx` and `which node` in a terminal, then make sure the directory containing `node` is included in the `PATH` value above. Common locations:
>
> - **Homebrew (macOS):** `/opt/homebrew/bin/npx`
> - **nvm:** `~/.nvm/versions/node/v20.x.x/bin/npx` (run `nvm which current` to find the exact path)
> - **System Node:** `/usr/local/bin/npx`
#### Claude Desktop (`claude_desktop_config.json`)
npx (zero install)
```json
{
"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)
```bash
npm install -g harness-mcp-v2
```
```json
{
"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)
```bash
claude mcp add harness -- npx harness-mcp-v2
```
node (local install)
```bash
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)
```json
{
"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)
```bash
npm install -g harness-mcp-v2
```
```json
{
"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)
```json
{
"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)
```bash
npm install -g harness-mcp-v2
```
```json
{
"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`:
```json
{
"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:
```json
{
"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](https://portkey.ai/features/mcp) for enterprise governance, cost tracking, and multi-LLM routing:
```json
{
"mcpServers": {
"harness": {
"command": "npx",
"args": ["harness-mcp-v2"],
"env": {
"HARNESS_API_KEY": "pat.xxx.xxx.xxx"
}
}
}
}
```
#### LiteLLM
Add to your [LiteLLM proxy config](https://docs.litellm.ai/docs/mcp):
```yaml
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](https://aigateway.envoyproxy.io/docs/0.5/capabilities/mcp/) via HTTP transport:
```bash
# 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](https://developer.konghq.com/mcp/) 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:
```bash
# 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:
```bash
# 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` (API key in config, used for all sessions) or `multi-user` (HTTP only, per-session credentials via `x-harness-api-key` and optional `x-harness-account-id` headers) |
| `HARNESS_API_KEY` | Yes* | -- | Harness personal access token or service account token. Required in `single-user` mode. Must NOT be set in `multi-user` mode |
| `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` | Harness API/UI base URL for local stdio or self-hosted HTTP deployments. Set this to environments such as `https://harness0.harness.io` when running the server yourself. It does not affect the managed `https://mcp.harness.io/mcp` hosted endpoint |
| `HARNESS_FME_API_KEY` | No | -- | Optional single-user/self-hosted FME/Split Admin credential used for `fme_` resources in **legacy (`workspace_id`) mode only**. This can be a legacy Split admin key or an FME-entitled Harness PAT/SAT. FME calls go directly to `api.split.io`, so hosted OAuth/service-routing credentials for Harness platform APIs do not authenticate these requests. Must not be set in `multi-user` mode; FME must use each session's `x-harness-api-key` credential. If unset, FME falls back to a non-placeholder `HARNESS_API_KEY` for self-hosted sessions. Harness-native (`org_id`+`project_id`) mode ignores this and uses the standard `HARNESS_API_KEY`/`HARNESS_BASE_URL` instead |
| `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](#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](#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 | -- | Bearer token required on `/mcp` HTTP routes when set. Required by default when HTTP transport binds to a non-loopback host |
| `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:**
```bash
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):
```bash
# 1. Create a venv and install FastAPI
python3 -m venv .venv-stub
.venv-stub/bin/pip install fastapi uvicorn
# 2. Start the stub (in-memory, cosine similarity, corpus + tenant filtering)
.venv-stub/bin/uvicorn stub-search-service:app --port 8082
# 3. Build the MCP server
pnpm build
# 4. Run the integration smoke test
node test-remote-provider.mjs
# Expected output:
# available: true
# indexed 2 docs
# entity search results: pipeline:ts-test score=... corpus=entities
# knowledge search results: schema:trigger score=...
# all-corpus search results: (merged, sorted by score)
# isolation check (other-acct, should be empty): PASS
# 5. Tear down
kill $(lsof -ti :8082)
```
The stub (`stub-search-service.py`) implements the same `/v1/health`, `/v1/ingest`, and `/v1/search` contract as the production search service. It uses a simple bag-of-chars embedding so no model download is required — results are semantically plausible but not production-quality.
### HTTPS Enforcement
`HARNESS_BASE_URL` must use HTTPS by default. If you set a non-HTTPS URL (e.g. `http://localhost:8080`), the server will refuse to start with:
```
HARNESS_BASE_URL must use HTTPS (got "http://..."). If you need HTTP for local development, set HARNESS_ALLOW_HTTP=true.
```
### Audit Logging
All registry-dispatched Harness API operations (`list`, `get`, `create`, `update`, `delete`, and `execute`) emit structured audit events when audit sinks are configured. Mutating events include the confirmation path used by elicitation or auto-approval when a confirmation context is present; read events currently omit confirmation metadata. Local metadata and schema discovery tools that bypass the registry, such as `harness_describe` and `harness_schema`, are not part of this audit stream. A stderr sink is registered by default but goes through the normal logger and obeys `LOG_LEVEL`; configure file or webhook sinks for durable audit collection:
- `HARNESS_AUDIT_FILE` appends newline-delimited JSON events for local collection.
- `HARNESS_AUDIT_WEBHOOK_URL` posts `{ "events": [...] }` batches to an HTTPS webhook, optionally with `HARNESS_AUDIT_WEBHOOK_TOKEN`. Failed batches are re-enqueued with bounded capacity and eventually dropped with a warning rather than blocking tool execution.
- `OTEL_EXPORTER_OTLP_ENDPOINT` enables audit spans when the optional OpenTelemetry peer dependencies are installed. The sink reuses an existing tracer provider when one is registered, otherwise it bootstraps a standalone OTLP exporter.
Each event includes the tool name, resource type, operation, identifiers, timestamp, risk, outcome, HTTP method/path, duration, and confirmation method when applicable. Audit sinks are best-effort telemetry; delivery issues are logged and never replay or change the underlying Harness API operation. For OTel setup details and span attributes, see [`specs/005-otel-audit-sink.md`](specs/005-otel-audit-sink.md).
## Tools Reference
The server exposes 11 MCP tools. Most API tools accept `org_id` and `project_id` as optional overrides — if omitted, they fall back to `HARNESS_ORG` and `HARNESS_PROJECT`. `harness_describe` is local metadata only and does not use org/project scope.
**URL support:** Most API-facing tools accept a `url` parameter — paste a Harness UI URL and the server auto-extracts org, project, resource type, resource ID, pipeline ID, and execution ID. `harness_describe` does not accept `url`.
**Scope support:** Resource types with account/org/project variants expose `supportedScopes` in `harness_describe`. Pass `resource_scope` when you need a specific level:
- `resource_scope: "account"` sends only `accountIdentifier`.
- `resource_scope: "org"` sends `accountIdentifier` and `orgIdentifier`.
- `resource_scope: "project"` sends account, org, and project identifiers.
Current multi-scope resources include `connector`, `service`, `environment`, `infrastructure`, `secret`, `file_store`, `template`, `policy`, and `policy_set`. If `resource_scope` is omitted, the registry uses the resource's default scope and configured defaults, except resources marked as optional scope may omit org/project unless explicitly passed. Harness URLs can also set the scope automatically when the path contains account-level or project-level context.
**Structured output:** Every tool declares an MCP `outputSchema`. `harness_list` normalizes list-like Harness responses into object-shaped structured content so strict clients can validate it: top-level arrays become `{ "items": [...], "total": <count>, "page": <page> }`, and common wrapper keys such as `content`, `data`, `body`, `objects`, or `features` are hoisted to `items` when needed. The text response still contains the compact JSON payload returned to all clients.
| Tool | Description |
| --------------------| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `harness_describe` | Discover available resource types, operations, and fields. No API call — returns local registry metadata. |
| `harness_schema` | Fetch exact YAML/JSON Schema definitions and examples for creating/updating resources. Pipeline/template schemas are bundled; connector, environment, service, secret, and infrastructure schemas are scope-aware entity schemas fetched from bundled snapshots or NG `/yaml-schema`; `release_process` and `release_activity` schemas are fetched live from RMG `/api/yamlSchema`. Supports deep drilling via `path`. |
| `harness_list` | List resources of a given type with filtering, search, and pagination. |
| `harness_get` | Get a single resource by its identifier. |
| `harness_create` | Create a new resource. Supports inline and remote (Git-backed) pipelines. Prompts for user confirmation via [elicitation](#elicitation). |
| `harness_update` | Update an existing resource. Supports inline and remote (Git-backed) pipelines. Prompts for user confirmation via [elicitation](#elicitation). |
| `harness_delete` | Delete a resource. Prompts for user confirmation via [elicitation](#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](#elicitation). For pipeline runs, use the runtime-input workflow below (supports `branch`/`tag`/`pr_number`/`commit_sha` shorthand expansion). |
| `harness_search` | Search across Harness resource types with a single query. Uses semantic routing (local `all-MiniLM-L6-v2` ONNX embeddings, 384-dim) to predict relevant resource types from a `knowledge` corpus indexed at startup — typically narrowing from ~163 types to 1–8 before scatter-gather. Falls back to full keyword scatter-gather when semantic confidence is low. Response includes `semantic_routed` and `types_skipped` when routing fires. See `docs/search-guidelines.md` for how to make new resource types discoverable. |
| `harness_diagnose` | Diagnose `pipeline`, `connector`, `delegate`, and `gitops_application` resources (aliases: `execution` -> `pipeline`, `gitops_app` -> `gitops_application`). For pipelines, returns stage/step timing and failure details; for connectors/delegates/GitOps apps, returns targeted health and troubleshooting signals. |
| `harness_status` | Get a real-time project health dashboard — recent executions, failure rates, and deep links. |
### Schema Lookup Workflow
Use `harness_schema` before creating or updating YAML-backed resources so agents can copy exact field names and constraints instead of guessing from prose.
- Bundled schemas include `pipeline`, `template`, `trigger`, `pipeline_v1`, `template_v1`, `inputSet_v1`, `overlayInputSet_v1`, and `agent-pipeline`.
- Entity schemas include `connector`, `environment`, `service`, `secret`, and `infrastructure`. They are scope-aware (`account`, `org`, or `project`) and require `org_id`/`project_id` when the selected scope requires them.
- Release Management definitions (`release_process`, `release_activity`) fetch live JSON Schema from RMG `/api/yamlSchema` (not bundled). Pass `scope`, `org_id`, and `project_id` when scoping to org or project.
- Vendored entity snapshots are used first when they match the runtime account; otherwise the tool falls back to the Harness NG `/yaml-schema` API and caches the result.
- Omit `path` for a field/section summary, then pass a dot-separated `path` to inspect a nested definition.
Examples:
```json
{ "resource_type": "pipeline", "path": "pipeline.stages" }
```
```json
{
"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:**
```json
{ "resource_type": "pipeline" }
```
**List organizations in the account:**
```json
{ "resource_type": "organization" }
```
**List projects in an organization:**
```json
{ "resource_type": "project", "org_id": "default" }
```
**List pipelines in a project:**
```json
{ "resource_type": "pipeline", "search_term": "deploy", "size": 10 }
```
**Get a specific service:**
```json
{ "resource_type": "service", "resource_id": "my-service-id" }
```
**Run a pipeline:**
```json
{
"resource_type": "pipeline",
"action": "run",
"resource_id": "my-pipeline",
"inputs": { "tag": "v1.2.3" },
"wait": true
}
```
**Toggle a feature flag:**
```json
{
"resource_type": "feature_flag",
"action": "toggle",
"resource_id": "new_checkout_flow",
"enable": true,
"environment": "production"
}
```
**Search across all resource types:**
```json
{ "query": "payment-service" }
```
**Diagnose an execution by ID (summary mode — default):**
```json
{ "execution_id": "abc123XYZ" }
```
**Diagnose from a Harness URL:**
```json
{ "url": "https://app.harness.io/ng/account/.../pipelines/myPipeline/executions/abc123XYZ/pipeline" }
```
**Diagnose connector connectivity:**
```json
{ "resource_type": "connector", "resource_id": "my_github_connector" }
```
**Diagnose delegate health:**
```json
{ "resource_type": "delegate", "resource_id": "delegate-us-east-1" }
```
**Diagnose a GitOps application (with options):**
```json
{
"resource_type": "gitops_application",
"resource_id": "checkout-app",
"options": { "agent_id": "gitops-agent-1" }
}
```
**Get the latest execution report for a pipeline:**
```json
{ "pipeline_id": "my-pipeline" }
```
**Full diagnostic mode with YAML and failed step logs:**
```json
{ "execution_id": "abc123XYZ", "summary": false }
```
**Summary mode with logs enabled (best of both):**
```json
{ "execution_id": "abc123XYZ", "include_logs": true }
```
**Get project health status:**
```json
{ "org_id": "default", "project_id": "my-project", "limit": 5 }
```
**List database schemas filtered by migration type:**
```json
{ "resource_type": "database_schema", "migration_type": "Liquibase" }
```
**List database instances for a schema:**
```json
{ "resource_type": "database_instance", "dbschema_id": "my_schema" }
```
**Get the resolved LLM authoring pipeline for a schema and instance:**
```json
{ "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:**
```json
{
"resource_type": "database_snapshot_object",
"dbschema_id": "my_schema",
"dbinstance_id": "prod_db",
"object_type": "Table"
}
```
**Get full snapshot metadata for specific named objects:**
```json
{
"resource_type": "database_snapshot_object",
"resource_id": "prod_db",
"params": {
"dbschema_id": "my_schema",
"object_type": "Table",
"object_names": ["users", "orders"]
}
}
```
### Pipeline Run Workflow (Recommended)
For v0 pipelines, use this sequence to reduce execution-time input errors:
1. **Discover required runtime inputs**
- `harness_get(resource_type="runtime_input_template", resource_id="<pipeline_id>")`
- The returned template shows `<+input>` placeholders that need values.
2. **Choose input strategy**
- **Simple variables:** pass flat key-value `inputs` (for example `{"branch":"main","env":"prod"}`).
- **Complex/structural inputs:** use `input_set_ids` (CI codebase/build blocks and nested template inputs are best handled this way).
- **CI codebase shorthand keys (pipeline run only):**
| Shorthand key | Expanded structure |
| ------------- | ------------------------------------------------------ |
| `branch` | `build.type=branch`, `build.spec.branch=<value>` |
| `tag` | `build.type=tag`, `build.spec.tag=<value>` |
| `pr_number` | `build.type=PR`, `build.spec.number=<value>` |
| `commit_sha` | `build.type=commitSha`, `build.spec.commitSha=<value>` |
- **Constraint:** shorthand expansion is skipped when `inputs.build` is already present (explicit `build` wins).
3. **Execute the run**
- `harness_execute(resource_type="pipeline", action="run", resource_id="<pipeline_id>", ...)`
- For Git-backed pipelines whose YAML should be loaded from a non-default branch, pass `params.pipeline_branch` (sent to Harness as `pipelineBranchName`):
```json
{
"resource_type": "pipeline",
"action": "run",
"resource_id": "deploy_app",
"params": { "pipeline_branch": "feature/new-stage" },
"inputs": { "branch": "main" },
"wait": true
}
```
4. **Optional: combine both**
- Use `input_set_ids` for the base shape and `inputs` for simple overrides.
For v1 pipelines:
1. Fetch `harness_get(resource_type="runtime_input_template_v1", resource_id="<pipeline_id>")`.
For Git-backed pipelines, pass `branch_name`, `connector_ref`, and `repo_name` through `params`.
2. Use each returned `inputs[].details.name` as a top-level key in `harness_execute.inputs`.
3. Run `harness_execute(resource_type="pipeline_v1", action="run", resource_id="<pipeline_id>", inputs={...})`.
The server wraps these values under an `inputs:` YAML root and sends the API's `inputs_yaml` body.
If required fields are unresolved, the tool returns a pre-flight error with expected keys and suggested input sets. You can inspect available shorthand mappings with `harness_describe(resource_type="pipeline")` (`executeActions.run.inputShorthands`).
### Dynamic Pipeline Execution
Use `pipeline_dynamic_execution.run` when an agent or external system generates the full v0 pipeline YAML at runtime and needs to run it against an existing Harness pipeline shell. This is not a replacement for normal `pipeline.run`: the saved v0 pipeline must already exist, account-level and pipeline-level **Allow Dynamic Execution** must be enabled, and the caller needs Edit plus Execute permissions on the pipeline.
```json
{
"resource_type": "pipeline_dynamic_execution",
"action": "run",
"resource_id": "deploy_app",
"body": {
"yaml": "pipeline:\n identifier: deploy_app\n name: Deploy App\n stages: []"
},
"params": {
"module_type": "CD",
"notes": "agent-generated dynamic run",
"notify_only_user": true
}
}
```
Constraints:
- `body` must be an object with a `yaml` field. Raw string bodies are rejected by the public `harness_execute` schema.
- `body.yaml` may be a YAML string or a JSON pipeline object; JSON is serialized to YAML before the request.
- Runtime `<+input>` placeholders are not resolved by this API. Submit fully resolved YAML.
- Input sets, selective stage execution, retry, and triggers are not supported by the dynamic execution endpoint.
- The action is `high_write` and uses the normal confirmation/auto-approval path. The response projects the API envelope to `{ "execution_id": "...", "status": "..." }` and includes an `openInHarness` execution link when scope data is available.
If Harness rejects the run as not enabled, check both the account-level Allow Dynamic Execution setting and the pipeline-level toggle under Pipeline -> Advanced Options -> Dynamic Execution Settings.
### Execution Input Forensics
Use `execution_inputs` after a run to inspect the merged input YAML that produced a specific execution. This is useful when a failure depends on input-set merging, Git-backed input set branches, or trigger/runtime values that are hard to reconstruct from the execution page alone.
```json
{
"resource_type": "execution_inputs",
"resource_id": "PLAN_EXECUTION_ID",
"params": {
"resolve_expressions": true,
"resolve_expressions_type": "RESOLVE_ALL_EXPRESSIONS"
}
}
```
The get response is projected to:
- `executionId` - the plan execution ID from `resource_id`.
- `inputSetYaml` - merged runtime input YAML used for the run, or `null`.
- `inputSetTemplateYaml` - input template at execution time, or `null`.
- `resolvedYaml` - expression-resolved YAML when `resolve_expressions=true`, otherwise usually `null`.
- `inputSetDetails` - contributing saved input sets as `{ identifier, name }` pairs.
- `inputSetBranchName` - source branch for Git-backed input sets, or `null`.
`execution_inputs` is get-only and read-risk. If `resolve_expressions` is omitted, the server omits the API query parameters and Harness uses its default `UNKNOWN` resolution mode.
### Pipeline Execute Wait Mode
For `pipeline.run`, `pipeline.retry`, and `pipeline_v1.run`, pass `wait: true` to let the server poll until the execution reaches a terminal status. This keeps a pipeline launch and status check in one tool call instead of asking the client or LLM to run a polling loop.
```json
{
"resource_type": "pipeline",
"action": "run",
"resource_id": "deploy_app",
"inputs": { "branch": "main" },
"wait": true,
"wait_timeout_seconds": 900,
"wait_poll_interval_seconds": 5
}
```
Wait mode behavior:
- Default timeout is 600 seconds; allowed range is 10 seconds to 7200 seconds.
- Initial poll interval defaults to 3 seconds, backs off by 1.5x, and caps at 30 seconds.
- On success or failure, the response includes fields such as `execution_id`, `execution_status`, `execution_terminal`, `execution_elapsed_ms`, and `execution_poll_count`.
- If the timeout fires, the original trigger still succeeded; the response includes `execution_timed_out: true` and `_wait.hint` with the last observed status.
- If polling fails after the trigger succeeds, the response includes `_wait.error` and a recheck hint. Do not blindly rerun the pipeline unless you have confirmed the first execution is not running.
- Failed terminal statuses include `_diagnose_hint` pointing to `harness_diagnose(resource_type="execution", options={execution_id: "..."})`.
**Ask the AI DevOps Agent to create a pipeline:**
```json
{
"prompt": "Create a pipeline that builds a Go app with Docker and deploys to Kubernetes",
"action": "CREATE_PIPELINE"
}
```
**Update a service via natural language:**
```json
{
"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):**
```json
// 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):**
```json
// 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):**
```json
// harness_create
{
"resource_type": "pipeline",
"body": {