# Cloud Harness MCP - Full Documentation > Complete Markdown documentation export for AI crawlers and LLM analysis. # Document: /agent-skill # Agent Skill: `cloudharness`
AI Crawler / Raw View: Fetch this page as raw Markdown at /agent-skill.md.
The `cloudharness` skill teaches AI coding agents how to autonomously navigate workspaces, execute tests, manage worktrees, and run tasks within Cloud Harness MCP. ## Installation ```bash # Skills CLI (recommended) npx skills add bestagentkits/cloud-harness-mcp --skill cloudharness --global # Claude Code Plugin claude plugin install cloud-harness@bestagentkits # OpenAI Codex Plugin codex plugin add cloud-harness@bestagentkits ``` ## Capabilities Taught to the Agent - **Safe Lifecycle:** Always keeping the `workspaceId`, closing stale shells before exit. - **Incremental Navigation:** Using `files_list` pagination and `grep_search` rather than recursive host dumping. - **Atomic File Edits:** Applying targeted patches with `files_apply_patch` instead of rewriting massive files. - **Clean Git Handoffs:** Verifying commit status before calling `git_push`. --- # Document: /ai-tools/antigravity # Google Antigravity
AI Crawler / Raw View: Fetch this page as raw Markdown at /ai-tools/antigravity.md.
## Configuration 1. In Antigravity, open the **MCP Servers** panel. 2. Click **Add Server Configuration**. 3. Set the endpoint: - **URL:** `https://api.harness.zuey.me/mcp` - **Headers:** `Authorization: Bearer ` 4. Confirm connectivity and ensure tools are active in your workspace sessions. --- # Document: /ai-tools/chatgpt # ChatGPT Custom MCP App
AI Crawler / Raw View: Fetch this page as raw Markdown at /ai-tools/chatgpt.md.
ChatGPT supports remote MCP connectors via Developer Mode using Managed OAuth. ## Prerequisites - ChatGPT Plus, Team, or Enterprise subscription with Developer Mode enabled. - Cloudflare Access SSO account (GitHub/Google) authorized for the Cloud Harness instance. ## Setup Instructions 1. In ChatGPT, navigate to **Settings** (or **Workspace Settings**) → **Apps** → **Create Custom App**. 2. Set the **Server URL** to: ```text https://harness.zuey.me/mcp ``` 3. Select **OAuth** as the authentication method. 4. Click **Connect & Authorize**. A browser popup will direct you to Cloudflare Access login. 5. Complete Google or GitHub authentication. 6. Once authorized, ChatGPT will scan and register the 52 Cloud Harness MCP tools. ::: tip Cloud Connection Note ChatGPT communicates directly from OpenAI infrastructure to `https://harness.zuey.me/mcp`. No local port-forwarding or reverse tunnel is required. ::: --- # Document: /ai-tools/claude-code # Claude Code
AI Crawler / Raw View: Fetch this page as raw Markdown at /ai-tools/claude-code.md.
Claude Code CLI natively connects to remote Streamable HTTP MCP servers. ## Quick Setup Export your dashboard-managed API key in your shell: ```bash export CLOUD_HARNESS_MCP_TOKEN="" ``` Register the server globally for your user: ```bash claude mcp add cloud-harness \ --transport http \ --url https://api.harness.zuey.me/mcp \ --header "Authorization=Bearer $CLOUD_HARNESS_MCP_TOKEN" \ --scope user ``` ## Verify Tools Start Claude Code and inspect available tools: ```bash claude /mcp ``` You should see `cloud-harness` listed with 52 available tools. --- # Document: /ai-tools/claude # Claude Desktop
AI Crawler / Raw View: Fetch this page as raw Markdown at /ai-tools/claude.md.
Claude Desktop can connect to Cloud Harness MCP through its configuration file using an HTTP proxy or static API key. ## Configuration Open your Claude Desktop configuration file: - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json` Add the `cloud_harness` entry under `mcpServers`: ```json { "mcpServers": { "cloud_harness": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-fetch", "https://api.harness.zuey.me/mcp" ], "env": { "AUTHORIZATION": "Bearer " } } } } ``` Restart Claude Desktop and check the hammer icon to verify tool availability. --- # Document: /ai-tools/codex # OpenAI Codex
AI Crawler / Raw View: Fetch this page as raw Markdown at /ai-tools/codex.md.
Codex uses TOML-based configuration for remote MCP endpoints. ## Configuration Set your environment variable: ```bash export CLOUD_HARNESS_MCP_TOKEN="" ``` Add the following block to `~/.codex/config.toml` (or `.codex/config.toml` in your project root): ```toml [mcp_servers.cloud_harness] url = "https://api.harness.zuey.me/mcp" bearer_token_env_var = "CLOUD_HARNESS_MCP_TOKEN" required = true tool_timeout_sec = 300 default_tools_approval_mode = "writes" ``` Restart Codex and verify: ```bash codex mcp list ``` --- # Document: /ai-tools/cursor # Cursor IDE
AI Crawler / Raw View: Fetch this page as raw Markdown at /ai-tools/cursor.md.
Cursor supports remote MCP servers over HTTP with custom request headers. ## Configuration 1. In Cursor, open **Cursor Settings** (`Cmd+,` on macOS, `Ctrl+,` on Windows/Linux). 2. Navigate to **Features** → **MCP Servers** → click **Add New MCP Server**. 3. Fill in the server details: - **Name:** `cloud-harness` - **Type:** `sse` or `http` - **Server URL:** `https://api.harness.zuey.me/mcp` - **Headers:** `{"Authorization": "Bearer "}` 4. Click **Save** and verify the status indicator turns green with 52 tools active. --- # Document: /ai-tools/gemini # Google Gemini CLI
AI Crawler / Raw View: Fetch this page as raw Markdown at /ai-tools/gemini.md.
## Configuration Set your authorization token in your shell environment: ```bash export CLOUD_HARNESS_MCP_TOKEN="" ``` Add the server to Gemini CLI settings or pass it via command-line flags: ```bash gemini mcp add cloud-harness \ --url https://api.harness.zuey.me/mcp \ --header "Authorization: Bearer $CLOUD_HARNESS_MCP_TOKEN" ``` --- # Document: /ai-tools/grok # Grok / xAI Responses API
AI Crawler / Raw View: Fetch this page as raw Markdown at /ai-tools/grok.md.
## API Configuration When using the xAI Responses API with remote tool calling, pass the static API gateway endpoint in your request tool definitions: ```json { "tools": [ { "type": "mcp", "mcp": { "server_url": "https://api.harness.zuey.me/mcp", "headers": { "Authorization": "Bearer " } } } ] } ``` Ensure only required write tools are allowlisted if running unattended tasks. --- # Document: /ai-tools/overview # AI Tools Integration Overview
AI Crawler / Raw View: Fetch this page as raw Markdown at /ai-tools/overview.md.
Cloud Harness MCP is compatible with all leading AI agent environments, IDEs, and coding tools. Depending on the client's authentication capabilities, you will connect using either **Managed OAuth** or a **Static API Key**. ## Compatibility Matrix | AI Tool / Client | Connection Mode | Target Endpoint | Configuration Surface | |---|---|---|---| | **ChatGPT** | Managed OAuth | `https://harness.zuey.me/mcp` | Web Settings → Apps → Create Custom MCP | | **Claude Desktop** | Managed OAuth / Gateway | `https://api.harness.zuey.me/mcp` | `claude_desktop_config.json` | | **Claude Code** | Static API Key | `https://api.harness.zuey.me/mcp` | `claude mcp add` / env var | | **Cursor IDE** | Static API Key | `https://api.harness.zuey.me/mcp` | Settings → Features → MCP | | **OpenAI Codex** | Static API Key | `https://api.harness.zuey.me/mcp` | `~/.codex/config.toml` | | **Gemini CLI** | Static API Key | `https://api.harness.zuey.me/mcp` | Environment header / CLI flags | | **Google Antigravity** | Static API Key | `https://api.harness.zuey.me/mcp` | Antigravity MCP Settings | | **Grok / xAI** | Static API Key | `https://api.harness.zuey.me/mcp` | Responses API tool definitions | --- ## Security Best Practices for Clients 1. **Keep Secrets Local:** Never paste API keys into prompts, git commits, or shared project configs. 2. **Use Environment Variables:** Reference tokens via environment variable substitution (e.g. `$CLOUD_HARNESS_MCP_TOKEN`) in tool config files whenever supported. 3. **Approval Modes:** For tools modifying the filesystem or Git (`files_write`, `git_push`), configure your AI client to require interactive confirmation before execution. --- # Document: /changelog # Release Changelog
AI Crawler / Raw View: Fetch this page as raw Markdown at /changelog.md.
Cloud Harness MCP follows [Semantic Versioning](https://semver.org/) and automated releases via [semantic-release](https://github.com/semantic-release/semantic-release). ## Recent Release Highlights - **v0.12.0:** Operator dashboard top header with theme toggle, nav collapse, and server status endpoint. - **v0.11.0:** Mission Control design system adoption with adaptive light/dark themes. - **v0.10.0:** Overview home dashboard, prune protection for local executor images. - **v0.9.0:** Dashboard UI/UX enhancements and metric panels. - **v0.8.0:** Profile page with signed-in account details. - **v0.7.0:** Dashboard-managed API keys for static header authentication. For the full detailed commit log and assets, visit the [GitHub Releases Page](https://github.com/bestagentkits/cloud-harness-mcp/releases). --- # Document: /concepts # Concepts & Glossary
AI Crawler / Raw View: Fetch this page as raw Markdown at /concepts.md.
## Core Concepts ### Workspace A bounded, temporary working directory created on the host filesystem at `/var/lib/cloud-harness/jobs//repo`. A workspace hosts a clean clone of the target Git repository and is mounted exclusively into a single executor container. ### Executor The Docker container (`cloud-harness-executor:local`) executing repository commands on behalf of the workspace. It runs with UID/GID 1000 (`node`), has no Docker socket, lacks host mount access, and is isolated by network namespaces. ### Runner The trusted central daemon (`apps/runner`) responsible for Docker container lifecycle, SQLite state persistence, GitHub App token brokerage, and audit recording. It is not exposed to the public Internet. ### Principal The authenticated identity invoking MCP tools. - In **Managed OAuth** mode, the principal is identified by Cloudflare Access claims (`sub`, `email`). - In **Static API Key** mode, the principal is identified by the API key record created in the dashboard. Workspaces are strictly isolated between different principals. ### Idempotency Key A unique client-generated string (8–128 characters) passed to mutating lifecycle operations such as `workspace_open`. If network connectivity drops, sending the same idempotency key recovers the existing workspace without repeating the clone operation. ### Time-To-Live (TTL) Every workspace enforces two hard lifetime boundaries: 1. **Wall TTL (default 900s / 15 min):** The maximum total duration a workspace may exist before automatic termination. 2. **Idle TTL (default 300s / 5 min):** The maximum period of inactivity between MCP tool calls before cleanup. ### Artifact Output files generated during workspace execution (logs, test reports, build outputs) that exceed inline MCP message bounds or need to survive container termination. Managed under `/var/lib/cloud-harness/artifacts`. ### Sibling Git Transfer Helper An ephemeral container spawned by the Runner to handle `git_fetch`, `git_pull`, and `git_push`. It mounts the workspace repository as a sibling, receives a 10-minute GitHub App installation token via `stdin`, talks only to `github.com`, and is immediately destroyed. --- # Document: /connect # Connecting MCP Clients
AI Crawler / Raw View: Fetch this page as raw Markdown at /connect.md.
Cloud Harness MCP provides two distinct connection lanes over **Streamable HTTP MCP**: ``` AI CLIENT │ ┌──────────────────┴──────────────────┐ ▼ ▼ Managed OAuth Lane Static API Key Lane https://harness.zuey.me/mcp https://api.harness.zuey.me/mcp │ │ Browser OAuth SSO Static Header Bearer (GitHub / Google via Access) Authorization: Bearer │ │ • ChatGPT Custom App • Cursor / Claude Code • Web connectors • Codex / Gemini CLI ``` ## Protocol Lane Comparison | Feature | Managed OAuth Lane | Static API Key Lane | |---|---|---| | **Public URL** | `https://harness.zuey.me/mcp` | `https://api.harness.zuey.me/mcp` | | **Authentication** | Cloudflare Access OAuth | Dashboard-managed API Key | | **Header** | Access JWT Cookie / Bearer | `Authorization: Bearer ` | | **Target Clients** | ChatGPT, Web App Connectors | Cursor, Claude Code, Codex, Antigravity | | **Key Expiry** | Session bound | 1–365 days (configurable) | | **Max Keys per Identity** | N/A | 10 active keys | --- ## Managing API Keys 1. Log into the **Operator Dashboard** at `https://harness.zuey.me/dashboard`. 2. Navigate to **API Keys** in the sidebar. 3. Click **Generate New Key**, specify an expiry (1–365 days) and label. 4. Copy the revealed key immediately. Store it securely in your local environment or credential vault. ::: danger One-Time Display API keys are shown exactly once upon creation and cannot be recovered from the database. If lost, revoke the old key and create a new one. ::: ## Supported AI Clients Select your AI coding tool for specific setup instructions: - [ChatGPT](/ai-tools/chatgpt) - [Claude Desktop](/ai-tools/claude) - [Claude Code](/ai-tools/claude-code) - [Cursor IDE](/ai-tools/cursor) - [OpenAI Codex CLI](/ai-tools/codex) - [Google Gemini CLI](/ai-tools/gemini) - [Google Antigravity](/ai-tools/antigravity) - [Grok / xAI](/ai-tools/grok) --- # Document: /dashboard/api-keys # API Keys
AI Crawler / Raw View: Fetch this page as raw Markdown at /dashboard/api-keys.md.
Static API keys allow IDE extensions (Cursor), local CLI agents (Claude Code, Codex), and scripts to authenticate against `https://api.harness.zuey.me/mcp` without an interactive browser OAuth flow. ## Key Rules & Limits - **Maximum Active Keys:** Up to 10 active keys per operator identity. - **Configurable Lifetime:** 1 day to 365 days. - **One-Time Secret Reveal:** Keys are shown only once upon generation. - **Instant Revocation:** Revoking a key denies any ongoing or future MCP requests instantly. ## Generating a Key 1. Navigate to **API Keys** in the dashboard. 2. Click **Create API Key**. 3. Choose a descriptive label (e.g. `Cursor Workstation M1`) and select an expiration timeframe. 4. Copy the key value immediately (`ch_live_...`). --- # Document: /dashboard/artifacts # Workspace Artifacts
AI Crawler / Raw View: Fetch this page as raw Markdown at /dashboard/artifacts.md.
Artifacts are output files generated by workspace tasks that survive container termination. ## Limits & Retention - **Max Artifact Size:** 16 MB per single file (configurable via `MAX_ARTIFACT_BYTES`). - **Principal Quota:** 128 MB total storage across all stored artifacts. - **Retention Window:** Default 24 hours (`ARTIFACT_RETENTION_SECONDS=86400`). Operators can browse and download generated artifacts directly from the dashboard. --- # Document: /dashboard/audit # Security Audit Trail
AI Crawler / Raw View: Fetch this page as raw Markdown at /dashboard/audit.md.
Cloud Harness MCP records an immutable audit log of all tool invocations and lifecycle operations in SQLite. ## Recorded Events - Workspace lifecycle (`workspace_open`, `workspace_close`, TTL terminations). - Command executions (`exec_run`, `shell_open`, shell exits). - File mutations (`files_write`, `files_delete`, `files_apply_patch`). - Git operations (`git_push`, branch switches, commits). - Security events (authentication failures, permission denials, rate limits). Logs record timestamps, authenticated principal ID, target repository, and exit status. --- # Document: /dashboard/github # GitHub App Configuration Guide
AI Crawler / Raw View: Fetch this page as raw Markdown at /dashboard/github.md.
Cloud Harness MCP uses a **GitHub App integration** to clone private repositories and execute origin-only pushes (`git_push`) without storing long-lived personal access tokens or SSH private keys inside container environments. --- ## How It Works ``` [ AI Agent in Executor Container ] │ │ 1. Calls git_push / workspace_open ▼ [ Trusted Runner Daemon ] │ 2. Exchanges App Private Key for a 10-minute repository token ▼ [ GitHub App Broker ] ──(token over STDIN)──► [ Ephemeral Git Helper ] │ │ 3. Pushes to github.com ▼ [ GitHub Private Repo ] ``` - **Ephemeral Access:** The token is valid for only 10 minutes and strictly scoped to the repository being operated on. - **Never Saved to Disk:** The token is streamed over `stdin` directly into Git's credential helper and is never written to `.git/config` or workspace files. - **Executor Confinement:** The container running your agent code has zero access to the GitHub App private key or control-plane credentials. --- ## Step 1: Decide Required Permissions Before creating the App in GitHub, choose the minimum required permission level: | Goal | Required Permission | |---|---| | **Clone, fetch, and pull private repos** | **Contents: Read-only** | | **Push ordinary code & branch commits** | **Contents: Read and write** | | **Push CI/CD workflows (`.github/workflows/`)** | **Contents: Read and write** + **Workflows: Read and write** | ::: tip Principle of Least Privilege Leave every other repository, organization, and user permission set to **No access**. ::: --- ## Step 2: Register the GitHub App 1. Open your GitHub account: - For a personal account: Go to [github.com/settings/apps](https://github.com/settings/apps). - For an organization: Go to `https://github.com/organizations//settings/apps`. 2. Click **New GitHub App**. 3. Fill in the basic details: - **GitHub App name:** `Cloud-Harness-` (must be globally unique across GitHub). - **Homepage URL:** `https://harness.agentkit.best` (or your repository URL). - **Callback URL:** Leave empty (unless using Access SSO OAuth redirect). - **Setup URL:** Leave empty. 4. **Webhook:** - Uncheck **Active** under the Webhook section (Cloud Harness does not require webhooks). 5. **Repository permissions:** - Set **Contents** to **Read and write** (or Read-only). - (Optional) Set **Workflows** to **Read and write** if your agents need to edit GitHub Actions. 6. **Where can this GitHub App be installed?:** - Select **Only on this account**. 7. Click **Create GitHub App**. --- ## Step 3: Download Private Key & Note IDs After creating the App: 1. **App ID:** Copy the numeric **App ID** displayed at the top of the General settings page (e.g. `123456`). 2. **App Slug / Name:** Note the URL slug of your app (e.g. `cloud-harness-operator`). 3. **Generate Private Key:** - Scroll down to the **Private keys** section. - Click **Generate a private key**. - A `.pem` file will automatically download to your machine. Keep this file secure; GitHub does not store it. --- ## Step 4: Install App on Selected Repositories 1. In your GitHub App settings, click **Install App** in the left sidebar. 2. Click **Install** next to your account or organization. 3. Choose **Only select repositories**. 4. Select the private repositories you want Cloud Harness agents to access. 5. Click **Install**. 6. After installation, GitHub redirects to a settings page URL such as: ```text https://github.com/settings/installations/78901234 ``` The trailing number (`78901234`) is your **Installation ID**. --- ## Step 5: Configure the Server Environment On your Cloud Harness production server or VPS: ### 1. Secure the Private Key File Place the downloaded `.pem` file on the host filesystem with strict root permissions: ```bash sudo install -d -m 700 -o root -g root /etc/cloud-harness-mcp sudo install -m 600 -o root -g root /path/to/downloaded-key.pem /etc/cloud-harness-mcp/github-app-private-key.pem ``` ### 2. Update Environment Variables (`.env`) In your server's `.env` or `/etc/cloud-harness-mcp/runtime.env`: ```dotenv # Your numeric GitHub App ID GITHUB_APP_ID=123456 # In owner-bearer mode: provide the numeric installation ID GITHUB_APP_INSTALLATION_ID=78901234 # In cloudflare-access mode: provide the app slug for dashboard linking GITHUB_APP_SLUG=cloud-harness-operator # Container mount path to the private key (inside the runner) GITHUB_APP_PRIVATE_KEY_FILE=/run/cloud-harness-secrets/github-app-private-key.pem ``` ### 3. Restart the Runner Restart containers so the runner picks up the new configuration: ```bash docker compose down && docker compose up -d ``` --- ## Step 6: Link in Operator Dashboard 1. Open the Operator Dashboard at `https://harness.zuey.me/dashboard`. 2. Navigate to **GitHub** in the left navigation rail. 3. If not already bound, click **Install / Link GitHub App**. 4. Authorize the application. The dashboard will display your active installation ID and bound repositories. --- ## Step 7: Verify Private Repository Access Test opening a private repository through your AI client (Cursor, Claude Code, etc.): ```json // Tool: workspace_open { "repositoryUrl": "https://github.com/my-org/private-repo.git", "idempotencyKey": "test-github-app-01" } ``` - Confirm `workspace_status` returns `status: "ready"`. - Test committing and pushing on a disposable branch: ```json // Tool: git_push { "workspaceId": "ws_...", "refspec": "HEAD:refs/heads/feature/test-branch" } ``` --- ## Troubleshooting | Issue | Cause | Resolution | |---|---|---| | **`Repository clone failed: unauthorized (404/401)`** | Repository not selected in GitHub App installation. | Go to [github.com/settings/installations](https://github.com/settings/installations), click **Configure** on your App, and add the repository to **Repository access**. | | **`Push failed: 403 Forbidden`** | App has Read-only permissions for `Contents`. | In App settings → **Permissions**, change **Contents** to **Read and write**. Then accept the permission update under your installation settings. | | **`Push touching workflows failed: 403`** | Missing `Workflows` permission when editing `.github/workflows/*`. | Add **Workflows: Read and write** to Repository Permissions in GitHub App settings. | | **`Token minting: UNAVAILABLE`** | App ID or Private Key mismatch. | Verify `GITHUB_APP_ID` matches GitHub App ID, and that the `.pem` file corresponds to that exact App. | | **Key rotation** | Need to rotate an existing key. | Generate a second key in GitHub first, update the `.pem` file on host, restart runner, then delete the old key in GitHub. | --- # Document: /dashboard/index # Operator Dashboard
AI Crawler / Raw View: Fetch this page as raw Markdown at /dashboard/index.md.
The **Mission Control** operator dashboard is available at `https://harness.zuey.me/dashboard`. It provides operators with live visibility into active workspaces, security audit logs, API key management, and GitHub App integrations. ## Key Sections - **[Workspaces](/dashboard/workspaces):** Inspect live Docker containers, open files, interactive shells, and close running workspaces. - **[Projects](/dashboard/projects):** Track repositories that have been cloned into workspaces. - **[API Keys](/dashboard/api-keys):** Create and revoke static API keys for IDE and local CLI tools. - **[GitHub App](/dashboard/github):** Manage GitHub App installation bindings for private repository cloning and push access. - **[Artifacts](/dashboard/artifacts):** Download and inspect workspace output files, build logs, and test results. - **[Audit Trail](/dashboard/audit):** Review immutable security logs of every tool invocation and authorization event. - **[Profile & Preferences](/dashboard/profile):** View signed-in identity details and configure server-persisted theme preferences (System, Light, Dark). ## Design System & Security Invariants - **Zero Secrets Rendered:** Container tokens, SSH private keys, and runner authentication credentials are never sent to or displayed in the dashboard. - **No Client Storage:** Theme preferences and session states are managed strictly via HttpOnly cookies and server session headers. - **Strict Content Security Policy (CSP):** Runs under `default-src 'none'` with no external CDN dependencies or tracking scripts. --- # Document: /dashboard/profile # Operator Profile & Appearance
AI Crawler / Raw View: Fetch this page as raw Markdown at /dashboard/profile.md.
## Identity Details The Profile panel displays your authenticated Cloudflare Access principal information: - **Email:** Account email address. - **Subject ID:** Unique Cloudflare Access subject identifier (`sub`). - **Session Expiry:** Time remaining on your active dashboard login session. ## Theme & Appearance Cloud Harness MCP supports three theme modes: - **System:** Automatically tracks your operating system `prefers-color-scheme`. - **Light:** High-contrast cool concrete industrial theme. - **Dark:** Tinted graphite console theme. ### Server-Side Persistence To comply with strict CSP rules forbidding browser storage (`localStorage`), your theme preference is persisted server-side via `PUT /api/v1/preferences` into a secure `ch-dashboard-theme` HttpOnly cookie. The dashboard shell injects `data-theme` directly on initial HTML delivery, preventing screen flicker on page load. --- # Document: /dashboard/projects # Projects
AI Crawler / Raw View: Fetch this page as raw Markdown at /dashboard/projects.md.
The Projects view displays distinct repository origins that have been opened by your operator account. ## Features - **Repository URLs:** See clean HTTPS repository URLs cloned by your agent sessions. - **Active Worktrees:** Inspect multiple concurrent branches or workspaces opened against the same repository. - **Git State:** Quick links to review recent commits and workspace history. --- # Document: /dashboard/workspaces # Workspaces
AI Crawler / Raw View: Fetch this page as raw Markdown at /dashboard/workspaces.md.
The Workspaces panel lists all currently active and recently closed workspace environments associated with your identity. ## Workspace Management - **Status Indicators:** View workspace lifecycle phase (`ready`, `busy`, `closed`, `error`). - **TTL Countdown:** Inspect remaining wall-clock TTL and idle timer before automatic cleanup. - **Resource Details:** Check memory limits, container status, and network mode (`none` or `bridge`). - **Force Close:** Immediately terminate a stuck container and permanently purge all temporary workspace files from the host disk. ::: danger Data Purge on Close Closing a workspace unmounts the Docker filesystem and recursively deletes the job directory. Ensure any necessary changes have been committed and pushed to origin before closing. ::: --- # Document: /faq # Frequently Asked Questions (FAQ)
AI Crawler / Raw View: Fetch this page as raw Markdown at /faq.md.
### What makes Cloud Harness MCP different from standard local MCP servers? Standard local MCP servers execute tools directly on your workstation with your user credentials and full filesystem permissions. Cloud Harness runs on a remote host inside dedicated, TTL-limited Docker containers, preventing rogue scripts or malicious dependencies from accessing your local machine. ### Can multiple people share one Cloud Harness instance? Yes, under a mutual trust model. Each operator logs in via Cloudflare Access SSO or uses distinct API keys. Workspaces and artifacts are isolated by principal ID. However, because containers share the host kernel, it should only be operated by trusted team members. ### How does Git push work without storing my SSH key in the container? When you call `git_push`, the runner provisions a short-lived ephemeral Alpine container, pipes an installation token from the GitHub App broker over `stdin`, pushes to `github.com`, and immediately destroys the container. The workspace container never sees the token. ### What happens if I forget to close a workspace? Workspaces automatically expire when their idle TTL (5 minutes without tool calls) or wall TTL (15 minutes total duration) is reached. All containers and temporary files are permanently cleaned up. --- # Document: /getting-started # Getting Started Guide
AI Crawler / Raw View: Fetch this page as raw Markdown at /getting-started.md.
## The 4-Step Lifecycle Working with Cloud Harness MCP follows a disciplined lifecycle: ``` 1. Open Workspace ──► 2. Inspect & Code ──► 3. Test & Commit ──► 4. Close Workspace ``` --- ### Step 1: Open a Workspace Call `workspace_open` with a credential-free HTTPS repository URL and a random idempotency key: ```json { "repositoryUrl": "https://github.com/my-org/my-project.git", "idempotencyKey": "init-2026-08-19-a1b2c3d4", "ref": "main" } ``` **Response:** ```json { "workspaceId": "ws_abcdef1234567890abcdef123456", "status": "ready", "expiresAt": "2026-08-19T10:15:00.000Z" } ``` ::: warning Keep the Workspace ID Save the returned `workspaceId`. All subsequent tool invocations (reading files, executing tests, running git operations) require this identifier. ::: --- ### Step 2: Inspect Files & Code List directory entries or search symbols: ```json // Tool: files_list { "workspaceId": "ws_abcdef1234567890abcdef123456", "path": "src" } // Tool: grep_search { "workspaceId": "ws_abcdef1234567890abcdef123456", "pattern": "export function authenticate" } ``` Edit files surgically or write new content: ```json // Tool: files_write { "workspaceId": "ws_abcdef1234567890abcdef123456", "path": "src/auth.ts", "content": "export function authenticate() { return true; }" } ``` --- ### Step 3: Run Tests & Commit Execute test commands inside the container: ```json // Tool: exec_run { "workspaceId": "ws_abcdef1234567890abcdef123456", "command": "npm test" } ``` Commit changes to the local branch: ```json // Tool: git_commit { "workspaceId": "ws_abcdef1234567890abcdef123456", "message": "feat(auth): implement token validation" } ``` Push to origin (requires GitHub App installation): ```json // Tool: git_push { "workspaceId": "ws_abcdef1234567890abcdef123456", "refspec": "HEAD:refs/heads/feature/auth" } ``` --- ### Step 4: Close the Workspace Always close your workspace when finished to release container and memory resources: ```json // Tool: workspace_close { "workspaceId": "ws_abcdef1234567890abcdef123456" } ``` Unclosed workspaces are automatically terminated when their idle TTL (5 min) or wall TTL (15 min) expires. --- # Document: /how-it-works # How It Works
AI Crawler / Raw View: Fetch this page as raw Markdown at /how-it-works.md.
Cloud Harness MCP is architected as a split control plane and execution runtime. This separation ensures that Internet-facing request handlers never possess Docker authority, while isolated containers executing repository code never inherit control plane credentials. ## System Architecture ``` PUBLIC INTERNET │ ┌────────────────────┴────────────────────┐ │ │ (Managed OAuth Lane) (Static API Key Lane) │ │ ▼ ▼ Cloudflare Access Cloudflare Worker Gateway │ │ └────────────────────┬────────────────────┘ │ ▼ Ingress Proxy (NGINX Loopback) │ ┌────────────┴────────────┐ │ CONTROL PLANE (TRUSTED)│ │ │ │ Stateless MCP API │ │ │ (RPC) │ │ ▼ │ │ Runner Service │ │ ├── SQLite State │ │ ├── Docker Auth │ │ └── GitHub Broker │ └────────────┬────────────┘ │ ┌─────────────────┴─────────────────┐ │ │ ▼ ▼ ┌───────────────────────┐ ┌───────────────────────┐ │ WORKSPACE EXECUTOR │ │ GIT TRANSFER HELPER │ │ • Non-root (UID 1000)│ │ • Ephemeral bare repo│ │ • Network: NONE (def)│ │ • GitHub App Token │ │ • No Docker socket │ │ passed via STDIN │ │ • TTL auto-cleanup │ │ • Origin-only push │ └───────────────────────┘ └───────────────────────┘ ``` ## Request Flow 1. **Client Connection:** The AI agent (Claude, ChatGPT, Cursor, etc.) connects to either the Managed OAuth endpoint (`https://harness.zuey.me/mcp`) or the Static API Key endpoint (`https://api.harness.zuey.me/mcp`). 2. **Ingress & Authentication:** The Ingress Proxy strips untrusted headers and forwards verified JSON-RPC requests over loopback to the stateless **MCP API**. 3. **Policy & Lifecycle Execution:** The API performs JSON schema validation against `TOOL_SCHEMA_BY_NAME` and issues an internal RPC to the **Runner**. The Runner validates principal permissions, checks concurrency bounds, and orchestrates the workspace container. 4. **Isolated Execution:** The **Workspace Executor** is spawned from `cloud-harness-executor:local`. The repository is cloned into a dedicated directory. Commands, file edits, and tasks execute inside this container under user `node` (UID 1000). 5. **Credential-Free Git Origin Transfer:** When `git_push` is invoked, the Runner starts an ephemeral helper container, streams a short-lived GitHub App installation token over `stdin`, pushes to GitHub origin, and immediately tears down the helper. The workspace executor never touches or observes the token. 6. **Result Normalization:** Structured output is truncated to safety bounds (e.g. 64KB per file read, 1MB per exec run) and returned through the Streamable HTTP connection. ## Key Invariants - **Default Network: `none`** — Workspace executors cannot access LAN or WAN unless explicitly started with `networkMode: "bridge"`. - **No Docker-in-Docker** — The Docker socket is never mounted into the workspace container. - **Idempotent Lifecycle** — `workspace_open` requires an `idempotencyKey`. If a client disconnects and retries with the same key, it attaches to the existing workspace rather than starting a duplicate clone. - **TTL Bounds** — Workspaces enforce both a wall-clock TTL (default 15 minutes) and an idle TTL (default 5 minutes). When expired, all files and containers are permanently scrubbed. --- # Document: /index
AI Crawlers: Browse clean Markdown for any page by appending .md to the URL (e.g. /getting-started.md), or read the complete index at /llms.txt and /llms-full.txt.
## Architecture at a Glance Cloud Harness separates request validation from Docker execution authority: ``` [ AI Client ] (OAuth / API Key) │ ▼ [ Credential-Free Ingress Proxy ] │ ▼ [ Stateless MCP API ] │ (Authenticated Private RPC) ▼ [ Trusted Runner ] ──► [ Docker Authority ] ──► [ Non-Root Executor ] │ │ ├─► [ SQLite State Store ] └─► [ Isolated Repo Clone ] └─► [ GitHub App Broker ] ─(stdin)─► [ Ephemeral Git Helper ] ``` ## Quick Endpoints | Protocol Lane | Public Endpoint | Authentication Method | |---|---|---| | **Managed OAuth** | `https://harness.zuey.me/mcp` | Cloudflare Access (GitHub/Google SSO) | | **Static API Key** | `https://api.harness.zuey.me/mcp` | `Authorization: Bearer ` | | **Operator Dashboard** | `https://harness.zuey.me/dashboard` | Browser SSO Session | | **Documentation** | `https://docs.harness.agentkit.best` | Public (Static Pages) | --- # Document: /installation # Installation & Prerequisites
AI Crawler / Raw View: Fetch this page as raw Markdown at /installation.md.
## System Requirements - **Linux OS:** Ubuntu 24.04 LTS (recommended) or Debian 12 - **Docker Engine:** 26.0+ with Docker Compose v2 - **Node.js:** 24.x LTS (for local CLI/development) - **RAM:** Minimum 4GB (8GB recommended for concurrent workspaces) - **Disk:** Minimum 40GB SSD for container images, jobs, and build caches ## Installing the Companion Agent Skill You can install the self-contained `cloudharness` agent skill directly from this repository using the [Skills CLI](https://www.npmjs.com/package/skills): ```bash # Project scope npx skills add bestagentkits/cloud-harness-mcp --skill cloudharness # Or global user scope npx skills add bestagentkits/cloud-harness-mcp --skill cloudharness --global ``` ### Claude Code Plugin Marketplace ```bash claude plugin marketplace add bestagentkits/cloud-harness-mcp claude plugin install cloud-harness@bestagentkits ``` ### OpenAI Codex Plugin ```bash codex plugin marketplace add bestagentkits/cloud-harness-mcp codex plugin add cloud-harness@bestagentkits ``` ## Running the Server with Docker Compose 1. **Clone the Repository:** ```bash git clone https://github.com/bestagentkits/cloud-harness-mcp.git cd cloud-harness-mcp ``` 2. **Configure Environment:** ```bash cp .env.example .env # Edit .env and supply your secrets (MCP_BEARER_TOKEN, RUNNER_TOKEN, etc.) ``` 3. **Build Images and Start Containers:** ```bash docker compose --profile images build executor-image api runner docker compose up -d ``` 4. **Verify Health:** ```bash curl http://127.0.0.1:3000/health # Returns: {"status":"healthy"} ``` --- # Document: /llms-info # AI Crawler Access & LLMs.txt
AI Crawler / Raw View: Fetch this page as raw Markdown at /llms-info.md.
Cloud Harness MCP documentation is built from the ground up for both human operators and autonomous AI agents. ## 1. Raw Markdown at `.md` URLs Every page on this site is mirrored as a clean, static Markdown file with frontmatter stripped. AI web scrapers and agents can directly fetch: ```text https://docs.harness.agentkit.best/.md ``` **Examples:** - `https://docs.harness.agentkit.best/getting-started.md` - `https://docs.harness.agentkit.best/how-it-works.md` - `https://docs.harness.agentkit.best/reference/tools.md` - `https://docs.harness.agentkit.best/reference/environment-variables.md` Cloudflare Pages serves these paths with `Content-Type: text/markdown; charset=utf-8` and CORS enabled (`Access-Control-Allow-Origin: *`). --- ## 2. Standard `llms.txt` The site exposes a standard [llms.txt](https://llmstxt.org/) index at the root: ```text https://docs.harness.agentkit.best/llms.txt ``` --- ## 3. Full Documentation Stream: `llms-full.txt` For ingestion into context windows or retrieval databases in a single HTTP request: ```text https://docs.harness.agentkit.best/llms-full.txt ``` --- # Document: /reference/environment-variables # Environment Variables
AI Crawler / Raw View: Fetch this page as raw Markdown at /reference/environment-variables.md.
Cloud Harness MCP is configured via environment variables supplied to the stateless **API**, the **Runner**, and the **Cloudflare Worker Gateway**. Copy `.env.example` to `.env` and replace all `change-me` placeholder secrets before starting services. ## Configuration Table | Variable | Default / Example | Required / Mode | Description & Purpose | |---|---|---|---| | `MCP_BEARER_TOKEN` | `change-me-at-least-32-random-characters` | **Required** | Copy to .env and replace every change-me value. Never commit real secrets. | | `RUNNER_TOKEN` | `change-me-independent-runner-token` | **Required** | Required configuration. | | `OWNER_ID` | `owner` | **Required** | Required configuration. | | `AUTH_MODE` | `owner-bearer` | **Required** | owner-bearer (default) or cloudflare-access. In Access mode, remove MCP_BEARER_TOKEN. | | `CLOUDFLARE_ACCESS_ISSUER` | `https://your-team.cloudflareaccess.com` | Optional | Optional configuration. | | `CLOUDFLARE_ACCESS_AUDIENCE` | `—` | Optional | Optional configuration. | | `CLOUDFLARE_ACCESS_JWKS_URL` | `https://your-team.cloudflareaccess.com/cdn-cgi/access/certs` | Optional | Optional configuration. | | `API_KEY_AUTH_ENABLED` | `false` | Optional | Optional managed API-key lane. Enable all four together only in cloudflare-access mode. The gateway audience belongs to a separate Access application scoped exactly to /mcp-api-key. | | `API_KEY_GATEWAY_ACCESS_AUDIENCE` | `—` | Optional | Optional configuration. | | `API_KEY_GATEWAY_SERVICE_SUBJECT` | `cf-service:base64url-cloudflare-service-token-client-id` | Optional | Optional configuration. | | `API_KEY_GATEWAY_PUBLIC_URL` | `https://api.harness.zuey.me/mcp` | Optional | Optional configuration. | | `ACCESS_LEGACY_OWNER_ID` | `owner` | Optional | Worker-only secrets CF_ACCESS_CLIENT_ID and CF_ACCESS_CLIENT_SECRET are configured with Wrangler, never here. Exact one-time legacy owner binding for the first Access rollout: | | `ACCESS_LEGACY_ISSUER` | `https://your-team.cloudflareaccess.com` | Optional | Optional configuration. | | `ACCESS_LEGACY_SUBJECT` | `—` | Optional | Optional configuration. | | `ACCESS_PRINCIPAL_RELINKS` | `[]` | Optional | Optional audited subject-rotation mappings, supplied as strict JSON: | | `API_PUBLIC_HOSTS` | `localhost,127.0.0.1,cloud-harness-mcp.46-250-239-227.sslip.io` | **Required** | Required configuration. | | `API_ALLOWED_ORIGINS` | `https://cloud-harness-mcp.46-250-239-227.sslip.io` | **Required** | Required configuration. | | `API_PORT` | `3000` | **Required** | Required configuration. | | `RUNNER_PORT` | `3001` | **Required** | Required configuration. | | `RUNNER_URL` | `http://runner:3001` | **Required** | Required configuration. | | `JOBS_ROOT` | `/var/lib/cloud-harness/jobs` | **Required** | Required configuration. | | `STATE_DB` | `/var/lib/cloud-harness/state/cloud-harness.db` | **Required** | Required configuration. | | `ARTIFACT_ROOT` | `/var/lib/cloud-harness/artifacts` | **Required** | Required configuration. | | `MAX_ARTIFACT_BYTES` | `16777216` | **Required** | Required configuration. | | `MAX_PRINCIPAL_ARTIFACT_BYTES` | `134217728` | **Required** | Required configuration. | | `ARTIFACT_RETENTION_SECONDS` | `86400` | **Required** | Required configuration. | | `EXECUTOR_IMAGE` | `cloud-harness-executor:local` | **Required** | Required configuration. | | `ALLOWED_GIT_HOSTS` | `github.com` | **Required** | Required configuration. | | `WORKSPACE_NETWORK_MODE` | `none` | **Required** | Required configuration. | | `WORKSPACE_WALL_TTL_SECONDS` | `900` | **Required** | Required configuration. | | `WORKSPACE_IDLE_TTL_SECONDS` | `300` | **Required** | Required configuration. | | `GITHUB_APP_ID` | `—` | Optional | Optional GitHub App repository access; required fields depend on AUTH_MODE: | | `GITHUB_APP_INSTALLATION_ID` | `—` | Optional | Required in owner-bearer mode; omit in Access mode, where each principal binds an installation: | | `GITHUB_APP_SLUG` | `—` | Optional | Required in Access mode for the installation redirect: | | `GITHUB_APP_PRIVATE_KEY_FILE` | `/run/cloud-harness-secrets/github-app-private-key.pem` | Optional | Production host file: /etc/cloud-harness-mcp/github-app-private-key.pem | | `SECRET_KEYRING_FILE` | `/run/cloud-harness-secrets/secret-keyring.json` | Optional | Versioned AES-256-GCM keyring JSON. Prefer the runner-only file form. | ## Security Guidelines 1. **Never commit `.env` files** or tokens into version control. 2. **Runner secrets isolation:** `RUNNER_TOKEN` and `SECRET_KEYRING_FILE` are passed only to the Runner container, never to the API or workspace executors. 3. **Managed OAuth vs Bearer:** When `AUTH_MODE=cloudflare-access`, remove `MCP_BEARER_TOKEN` and configure `CLOUDFLARE_ACCESS_*` variables instead. 4. **Executor Isolation:** Executors never inherit host environment variables or control plane tokens. --- # Document: /reference/git-transfer # Git Transfer Semantics
AI Crawler / Raw View: Fetch this page as raw Markdown at /reference/git-transfer.md.
Cloud Harness MCP enforces strict isolation between repository execution containers and GitHub credentials. ## The Transfer Problem Standard coding agent sandboxes either: 1. Embed the user's GitHub Personal Access Token or SSH key directly into the container filesystem or environment (allowing arbitrary scripts or dependencies to steal the credential), or 2. Forbid remote push entirely, requiring manual user intervention. ## The Sibling Helper Solution Cloud Harness solves this with an **ephemeral sibling Git helper**: ``` [ Workspace Container ] (no network, no token) ▲ │ (local disk mount) ▼ [ Host Repo Directory ] ▲ │ (local disk mount) ▼ [ Sibling Git Helper ] ──(token over stdin)──► [ github.com:owner/repo.git ] ``` 1. The agent invokes `git_push(refspec, forceWithLease?, expectedRemoteOid?)`. 2. The Runner starts an ephemeral Alpine container with network access scoped only to `github.com`. 3. The Runner streams an installation token over `stdin` into the helper's `git-credential` helper. 4. The helper executes the push against the remote origin and immediately exits. 5. The container is destroyed. The token is never written to disk or `.git/config`. --- # Document: /reference/limits # Protocol Limits & Bounds
AI Crawler / Raw View: Fetch this page as raw Markdown at /reference/limits.md.
To prevent unbounded memory growth and buffer overflow attacks, Cloud Harness MCP enforces deterministic bounds across all operations: ## Resource Bounds | Surface | Limit | Configurable Env Var | |---|---|---| | **Max File Read Size** | 256 KB (default 64 KB) | `files_read.limit` | | **Max Command Exec Output** | 1 MB | `exec_run` buffer | | **Max Artifact Size** | 16 MB | `MAX_ARTIFACT_BYTES` | | **Total Principal Artifacts** | 128 MB | `MAX_PRINCIPAL_ARTIFACT_BYTES` | | **Workspace Wall TTL** | 900s (15 min) | `WORKSPACE_WALL_TTL_SECONDS` | | **Workspace Idle TTL** | 300s (5 min) | `WORKSPACE_IDLE_TTL_SECONDS` | | **Max File List Entries** | 500 entries / page | `files_list.limit` | | **Max Active API Keys** | 10 per identity | Hard bound | --- # Document: /reference/sessions-and-tasks # Sessions and Task DAGs
AI Crawler / Raw View: Fetch this page as raw Markdown at /reference/sessions-and-tasks.md.
## Persistent PTY Sessions For commands requiring user interaction, REPLs, or live monitoring: - `shell_open`: Spawns a persistent pseudoterminal (PTY) session inside the workspace. - `shell_io`: Sends input (keystrokes, text) and reads streaming output. - `shell_close`: Gracefully signals or terminates the process. ## Task Graphs (DAGs) For complex multi-stage builds or benchmarks: - `tasks_run`: Submits a parallel dependency graph of tasks. - `tasks_status`: Polls execution status and outputs of tasks. - `tasks_cancel`: Cancels running or pending graph stages. - `tasks_graph`: Inspects dependency topology. --- # Document: /reference/tools # Tools Reference
AI Crawler / Raw View: Fetch this page as raw Markdown at /reference/tools.md.
Cloud Harness MCP exposes **52 tools** across six operational domains. Every tool executes strictly inside a sandboxed, TTL-limited Docker container with non-root privileges and default network isolation. ## Security & Capability Badges - readOnly: Tool inspects state without mutating workspace filesystem or runtime. - destructive: Tool modifies files, processes, or git state. - `idempotent`: Calling the tool repeatedly with identical inputs yields equivalent state. - openWorld: Tool may interact with network or long-running execution boundaries. --- ## Workspace Lifecycle Tools for opening, inspecting, listing, and closing isolated TTL-bound workspaces. ### `workspace_open` **Open workspace** Clone an approved HTTPS repository and start an owner-bound, TTL-limited coding workspace. **Attributes:** `idempotent` · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `repositoryUrl` | `string` | **Yes** | format: `uri` | | `ref` | `string` | No | length: 1–255 | | `idempotencyKey` | `string` | **Yes** | pattern: `^[A-Za-z0-9._:-]+$`, length: 8–128 | | `networkMode` | `"none"` \| `"bridge"` | No | — | | `environmentId` | `string` | No | pattern: `^env_[A-Za-z0-9_-]{20,80}$` | | `confirmEnvironmentInjection` | `true` | No | — | ### `workspace_list` **List workspaces** List owner-visible workspace records with bounded cursor pagination. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `cursor` | `string` | No | max length: 256 | | `limit` | `integer` | **Yes** | range: 1–500, default: `100` | ### `workspace_status` **Workspace status** Read the lifecycle state and metadata for one workspace. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | ### `workspace_close` **Close workspace** Stop a workspace executor and permanently remove all workspace files, including unpushed commits. **Attributes:** destructive · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | ## Files and Code Intelligence Tools for navigating directory trees, reading/writing files, structural search, and symbol analysis. ### `files_list` **List files** List one workspace directory with bounded cursor pagination. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `path` | `string` | **Yes** | length: 1–1024, default: `"."` | | `cursor` | `string` | No | max length: 256 | | `limit` | `integer` | **Yes** | range: 1–500, default: `100` | ### `files_read` **Read file** Read a bounded byte range from a workspace file with its size and SHA-256. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `path` | `string` | **Yes** | length: 1–1024 | | `offset` | `integer` | **Yes** | range: 0–9007199254740991, default: `0` | | `limit` | `integer` | **Yes** | range: 1–262144, default: `65536` | ### `files_write` **Write file** Atomically create or replace a workspace file, optionally guarded by its current SHA-256. **Attributes:** destructive · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `path` | `string` | **Yes** | length: 1–1024 | | `content` | `string` | **Yes** | max length: 1048576 | | `expectedSha256` | `string` | No | length: 64–64 | ### `files_apply_patch` **Apply text patch** Replace one unique exact text occurrence, optionally guarded by the file SHA-256. **Attributes:** destructive · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `path` | `string` | **Yes** | length: 1–1024 | | `oldText` | `string` | **Yes** | max length: 262144 | | `newText` | `string` | **Yes** | max length: 262144 | | `expectedSha256` | `string` | No | length: 64–64 | ### `files_delete` **Delete file or directory** Delete one workspace file or directory, with explicit recursion and optional file hash guard. **Attributes:** destructive | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `path` | `string` | **Yes** | length: 1–1024 | | `recursive` | `boolean` | **Yes** | default: `false` | | `expectedSha256` | `string` | No | length: 64–64 | ### `files_move` **Move file or directory** Move or rename one workspace entry, optionally overwriting an existing file. **Attributes:** destructive | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `source` | `string` | **Yes** | length: 1–1024 | | `destination` | `string` | **Yes** | length: 1–1024 | | `overwrite` | `boolean` | **Yes** | default: `false` | ### `files_mkdir` **Create directory** Create one workspace directory, including missing parents by default. **Attributes:** `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `path` | `string` | **Yes** | length: 1–1024 | | `recursive` | `boolean` | **Yes** | default: `true` | ### `grep_search` **Search workspace** Search workspace text with a bounded regular expression and optional path or glob filter. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `pattern` | `string` | **Yes** | length: 1–4096 | | `path` | `string` | **Yes** | length: 1–1024, default: `"."` | | `glob` | `string` | No | max length: 512 | | `maxResults` | `integer` | **Yes** | range: 1–500, default: `100` | ### `symbols_search` **Find symbol definitions** Find bounded indexed symbol definitions by case-insensitive substring. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `query` | `string` | **Yes** | length: 1–256 | | `path` | `string` | **Yes** | length: 1–1024, default: `"."` | | `language` | `string` | No | pattern: `^[A-Za-z0-9_+#.-]{1,40}$` | | `maxResults` | `integer` | **Yes** | range: 1–500, default: `100` | ### `symbols_references` **Find lexical symbol references** Find bounded lexical whole-word occurrences of a symbol in workspace text. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `symbol` | `string` | **Yes** | length: 1–256 | | `path` | `string` | **Yes** | length: 1–1024, default: `"."` | | `glob` | `string` | No | max length: 512 | | `maxResults` | `integer` | **Yes** | range: 1–500, default: `100` | ## Commands and Shells Tools for running isolated non-root commands and persistent interactive PTY shell sessions. ### `exec_run` **Run command** Run one bounded shell command in the workspace and return captured output. **Attributes:** destructive · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `command` | `string` | **Yes** | length: 1–32768 | | `cwd` | `string` | **Yes** | length: 1–1024, default: `"."` | | `timeoutMs` | `integer` | **Yes** | range: 100–300000, default: `60000` | | `maxOutputBytes` | `integer` | **Yes** | range: 1024–1048576, default: `262144` | ### `shell_open` **Open shell** Open an idempotent ephemeral interactive shell in the workspace. **Attributes:** `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `cwd` | `string` | **Yes** | length: 1–1024, default: `"."` | | `idempotencyKey` | `string` | **Yes** | pattern: `^[A-Za-z0-9._:-]+$`, length: 8–128 | ### `shell_io` **Use shell** Send input to or poll bounded output from an open interactive shell. **Attributes:** destructive · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `shellId` | `string` | **Yes** | pattern: `^sh_[A-Za-z0-9_-]{20,80}$` | | `input` | `string` | No | max length: 65536 | | `cursor` | `string` | No | max length: 256 | | `waitMs` | `integer` | **Yes** | range: 0–5000, default: `100` | ### `shell_close` **Close shell** Terminate an interactive shell and release its in-memory state. **Attributes:** destructive · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `shellId` | `string` | **Yes** | pattern: `^sh_[A-Za-z0-9_-]{20,80}$` | ## Sessions and Tasks Tools for long-running agent sessions and directed acyclic task graph workflows. ### `sessions_list` **List coding sessions** List named coding sessions in a workspace with bounded cursor pagination. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `cursor` | `string` | No | max length: 256 | | `limit` | `integer` | **Yes** | range: 1–500, default: `100` | ### `sessions_open` **Open coding session** Open an idempotent named coding session in the workspace. **Attributes:** `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `name` | `string` | **Yes** | pattern: `^[A-Za-z0-9._-]{1,80}$` | | `cwd` | `string` | **Yes** | length: 1–1024, default: `"."` | | `idempotencyKey` | `string` | **Yes** | pattern: `^[A-Za-z0-9._:-]+$`, length: 8–128 | ### `sessions_io` **Use coding session** Send input to or poll bounded output from a named coding session. **Attributes:** destructive · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `sessionId` | `string` | **Yes** | pattern: `^sess_[A-Za-z0-9_-]{20,80}$` | | `input` | `string` | No | max length: 65536 | | `cursor` | `string` | No | max length: 256 | | `waitMs` | `integer` | **Yes** | range: 0–5000, default: `100` | ### `sessions_close` **Close coding session** Terminate a coding session and release its in-memory state. **Attributes:** destructive · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `sessionId` | `string` | **Yes** | pattern: `^sess_[A-Za-z0-9_-]{20,80}$` | ### `tasks_list` **List tasks** List managed background task records with bounded cursor pagination. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `cursor` | `string` | No | max length: 256 | | `limit` | `integer` | **Yes** | range: 1–500, default: `100` | ### `tasks_run` **Run task** Start an idempotent managed command task with optional task dependencies. **Attributes:** destructive · `idempotent` · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `command` | `string` | **Yes** | length: 1–32768 | | `cwd` | `string` | **Yes** | length: 1–1024, default: `"."` | | `idempotencyKey` | `string` | **Yes** | pattern: `^[A-Za-z0-9._:-]+$`, length: 8–128 | | `timeoutMs` | `integer` | **Yes** | range: 100–86400000, default: `900000` | | `dependsOn` | string[] | **Yes** | default: `[]` | ### `tasks_status` **Task status** Read one managed task state and its next bounded output chunk. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `taskId` | `string` | **Yes** | pattern: `^task_[A-Za-z0-9_-]{20,80}$` | | `cursor` | `string` | No | max length: 256 | ### `tasks_cancel` **Cancel task** Terminate a managed task process group without rolling back completed effects. **Attributes:** destructive · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `taskId` | `string` | **Yes** | pattern: `^task_[A-Za-z0-9_-]{20,80}$` | ### `tasks_graph` **Read task dependency graph** Read the current managed-task dependency graph for a workspace. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | ## Git and Worktrees Local repository version control, branch management, worktree isolation, and credential-isolated origin transfer. ### `git_status` **Git status** Read branch, index, and working-tree status for the workspace repository. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | ### `git_diff` **Git diff** Read a bounded staged or unstaged Git diff, optionally narrowed to one path. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `staged` | `boolean` | **Yes** | default: `false` | | `path` | `string` | No | length: 1–1024 | ### `git_log` **Git log** Read bounded recent commit metadata from the workspace repository. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `limit` | `integer` | **Yes** | range: 1–100, default: `20` | ### `git_branch` **Manage Git branches** List, create, or delete a local Git branch with constrained ref arguments. **Attributes:** destructive | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `action` | `"list"` \| `"create"` \| `"delete"` | **Yes** | — | | `name` | `string` | No | length: 1–255 | | `startPoint` | `string` | No | length: 1–255 | | `force` | `boolean` | **Yes** | default: `false` | ### `git_checkout` **Checkout Git ref** Check out an existing Git ref or create and check out a local branch. **Attributes:** destructive | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `ref` | `string` | **Yes** | length: 1–255 | | `create` | `boolean` | **Yes** | default: `false` | ### `git_add` **Stage Git changes** Stage either explicit workspace paths or all tracked and untracked changes. **Attributes:** `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `all` | `boolean` | **Yes** | default: `false` | | `paths` | string[] | **Yes** | default: `[]` | ### `git_commit` **Create Git commit** Create an unsigned local Git commit with an explicit author and message. | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `message` | `string` | **Yes** | length: 1–10000 | | `authorName` | `string` | **Yes** | length: 1–200 | | `authorEmail` | `string` | **Yes** | format: `email`, pattern: `^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$` | | `all` | `boolean` | **Yes** | default: `false` | ### `git_fetch` **Fetch Git refs** Fetch a constrained source ref from origin through the trusted repository broker. **Attributes:** openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `remote` | `"origin"` | **Yes** | default: `"origin"` | | `refspec` | `string` | No | length: 1–255 | ### `git_pull` **Pull Git changes** Integrate an origin branch using ff-only, merge, or rebase strategy. **Attributes:** destructive · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `remote` | `"origin"` | **Yes** | default: `"origin"` | | `branch` | `string` | No | length: 1–255 | | `strategy` | `"ff-only"` \| `"merge"` \| `"rebase"` | **Yes** | default: `"ff-only"` | ### `git_push` **Push Git changes** Push a constrained branch refspec to origin, with optional explicit force-with-lease. **Attributes:** destructive · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `remote` | `"origin"` | **Yes** | default: `"origin"` | | `refspec` | `string` | No | length: 1–512 | | `forceWithLease` | `boolean` | **Yes** | default: `false` | | `expectedRemoteOid` | `string` | No | pattern: `^(?:[0-9a-f]{40}|[0-9a-f]{64})$` | ### `git_merge` **Merge Git ref** Merge a Git ref with an explicit fast-forward policy and optional message. **Attributes:** destructive | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `ref` | `string` | **Yes** | length: 1–255 | | `fastForward` | `"allow"` \| `"only"` \| `"never"` | **Yes** | default: `"allow"` | | `message` | `string` | No | length: 1–10000 | ### `git_rebase` **Manage Git rebase** Start, continue, or abort a local Git rebase with constrained ref arguments. **Attributes:** destructive | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `action` | `"start"` \| `"continue"` \| `"abort"` | **Yes** | — | | `upstream` | `string` | No | length: 1–255 | ### `worktrees_list` **List worktrees** List managed Git worktrees and their current branch or HEAD state. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | ### `worktrees_create` **Create worktree** Create a named managed worktree, optionally with a new local branch. | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `name` | `string` | **Yes** | pattern: `^[A-Za-z0-9._-]{1,80}$` | | `ref` | `string` | **Yes** | length: 1–255 | | `createBranch` | `boolean` | **Yes** | default: `false` | ### `worktrees_remove` **Remove worktree** Remove one named managed worktree, optionally discarding dirty state. **Attributes:** destructive | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `name` | `string` | **Yes** | pattern: `^[A-Za-z0-9._-]{1,80}$` | | `force` | `boolean` | **Yes** | default: `false` | ## Repository Extensions Skills execution, workspace hooks, persistent memory, and repository-defined deployment operations. ### `skills_list` **List skills** List repository-provided agent skills discovered in the workspace. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | ### `skills_read` **Read skill** Read bounded instructions for one repository-provided agent skill. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `name` | `string` | **Yes** | pattern: `^[A-Za-z0-9._:-]{1,120}$` | ### `skills_run` **Run skill script** Execute one reviewed script packaged by a repository-provided skill. **Attributes:** destructive · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `name` | `string` | **Yes** | pattern: `^[A-Za-z0-9._:-]{1,120}$` | | `script` | `string` | **Yes** | pattern: `^[A-Za-z0-9._-]{1,120}$` | | `args` | string[] | **Yes** | default: `[]` | | `timeoutMs` | `integer` | **Yes** | range: 100–300000, default: `60000` | ### `hooks_list` **List hooks** List repository-defined Cloud Harness automation hooks without running them. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | ### `hooks_run` **Run hook** Execute one named repository-defined hook as a bounded shell command. **Attributes:** destructive · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `name` | `string` | **Yes** | pattern: `^[A-Za-z0-9._-]{1,120}$` | | `timeoutMs` | `integer` | **Yes** | range: 100–300000, default: `60000` | ### `memories_list` **List memories** List repository-local Cloud Harness memory note names. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | ### `memories_read` **Read memory** Read one bounded repository-local Cloud Harness memory note. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `name` | `string` | **Yes** | pattern: `^[A-Za-z0-9._-]{1,120}$` | ### `memories_write` **Write memory** Create or replace one repository-local Cloud Harness memory note. **Attributes:** destructive · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `name` | `string` | **Yes** | pattern: `^[A-Za-z0-9._-]{1,120}$` | | `content` | `string` | **Yes** | max length: 262144 | ### `deployments_list` **List deployment targets** List repository-defined deployment targets without running them. **Attributes:** readOnly · `idempotent` | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | ### `deployments_run` **Run deployment target** Execute one named repository-defined deployment target with external-effect risk. **Attributes:** destructive · openWorld | Parameter | Type | Required | Constraints & Notes | |---|---|---|---| | `workspaceId` | `string` | **Yes** | pattern: `^ws_[A-Za-z0-9_-]{20,80}$` | | `name` | `string` | **Yes** | pattern: `^[A-Za-z0-9._-]{1,120}$` | | `timeoutMs` | `integer` | **Yes** | range: 100–300000, default: `60000` | --- # Document: /security-model # Security & Threat Model
AI Crawler / Raw View: Fetch this page as raw Markdown at /security-model.md.
## Intended Trust Model Cloud Harness MCP is intentionally a **private, single-owner remote coding harness**. It allows arbitrary repository-controlled execution inside a constrained executor, but it is **not a hostile multi-tenant sandbox**. ## Defensive Layers ### 1. Ingress & Control Plane Isolation - The Ingress Proxy is the only service bound to external loopback. - The API and Runner never publish host ports directly. - The API has no access to the Docker socket or host filesystem mounts. ### 2. Executor Confinement - **Non-Root User:** Containers execute as UID 1000 (`node`). - **No Docker Authority:** No socket mount or privileged capabilities. - **Default Network `none`:** Outbound network is disabled unless explicitly requested as `bridge`. ### 3. Credential Safety - Private clone and push tokens exist only in memory during the lifetime of the ephemeral Git helper. - Tokens are streamed over `stdin` and never stored in environment variables, configuration files, or repository commit history. --- # Document: /self-host # Self-Hosting & Deployment
AI Crawler / Raw View: Fetch this page as raw Markdown at /self-host.md.
Cloud Harness MCP is designed for straightforward self-hosting on any modern Linux VPS or cloud instance. ## Deployment Stack - **Compose Stack:** `compose.production.yaml` defines the loopback ingress, stateless API, and runner daemon. - **Systemd Service:** `deploy/systemd/cloud-harness-mcp.service` ensures auto-restart and proper shutdown lifecycle. - **NGINX Reverse Proxy:** Terminates SSL, manages WebSocket upgrade for PTY sessions, and strips untrusted upstream headers. ## Production Runbook 1. **Bootstrap VPS:** Run `deploy/scripts/bootstrap-vps.sh` to install Docker, systemd unit, and permissions. 2. **Deploy Release:** Run `deploy/scripts/deploy-release.sh ` to build and start production containers. 3. **Canary Verification:** Run `scripts/deploy-canary.mjs` to execute an automated end-to-end workspace test against the newly deployed instance. --- # Document: /troubleshooting # Troubleshooting & Diagnostics
AI Crawler / Raw View: Fetch this page as raw Markdown at /troubleshooting.md.
## Common Issues & Fixes ### 1. `docker: No such image: cloud-harness-executor:local` **Cause:** Local executor image was pruned by a host Docker cleanup or never built. **Fix:** Rebuild the image from the project root: ```bash docker compose --profile images build executor-image ``` --- ### 2. `repository clone failed: unauthorized` **Cause:** Attempting to clone a private repository without a valid GitHub App installation. **Fix:** 1. Open the Operator Dashboard → **GitHub**. 2. Click **Install GitHub App** and authorize the target repository. 3. Ensure the repository URL matches the format `https://github.com/owner/repo.git`. --- ### 3. `workspace expired: TTL exceeded` **Cause:** The workspace reached its 15-minute wall-clock limit or 5-minute idle limit. **Fix:** Workspaces are ephemeral by design. Re-open a workspace using `workspace_open` with a fresh idempotency key. --- ### 4. API Key Denied (`401 Unauthorized`) **Cause:** Expired key, revoked key, or key used against the Managed OAuth URL instead of the gateway. **Fix:** - Ensure the client URL is `https://api.harness.zuey.me/mcp` (NOT `https://harness.zuey.me/mcp`). - Verify key validity in the Dashboard under **API Keys**.