{
  "markdown": "<!-- mcp-name: io.github.imatza-rh/mcp-zuul -->\n\n# mcp-zuul\n\n<p align=\"center\">\n  <a href=\"https://pypi.org/project/mcp-zuul/\"><img src=\"https://img.shields.io/pypi/v/mcp-zuul\" alt=\"PyPI\"></a>\n  <a href=\"https://pypi.org/project/mcp-zuul/\"><img src=\"https://img.shields.io/pypi/pyversions/mcp-zuul\" alt=\"Python\"></a>\n  <a href=\"https://github.com/imatza-rh/mcp-zuul/blob/main/LICENSE\"><img src=\"https://img.shields.io/github/license/imatza-rh/mcp-zuul\" alt=\"License\"></a>\n  <a href=\"https://github.com/imatza-rh/mcp-zuul/actions/workflows/ci.yml\"><img src=\"https://github.com/imatza-rh/mcp-zuul/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"></a>\n  <a href=\"https://glama.ai/mcp/servers/imatza-rh/mcp-zuul\"><img src=\"https://glama.ai/mcp/servers/imatza-rh/mcp-zuul/badges/score.svg\" alt=\"MCP\"></a>\n  <a href=\"https://pepy.tech/projects/mcp-zuul\"><img src=\"https://img.shields.io/pepy/dt/mcp-zuul\" alt=\"Downloads\"></a>\n  <a href=\"https://codecov.io/gh/imatza-rh/mcp-zuul\"><img src=\"https://codecov.io/gh/imatza-rh/mcp-zuul/graph/badge.svg\" alt=\"codecov\"></a>\n  <a href=\"https://hifriendbot.com/ai-list/zuul-ci-by-imatza-rh/\"><img src=\"https://hifriendbot.com/ai-list/badge/zuul-ci-by-imatza-rh.svg\" alt=\"Listed on AiList\"></a>\n</p>\n\nDebug build failures by asking questions, not clicking through web UIs. An [MCP](https://modelcontextprotocol.io/) server for [Zuul CI](https://zuul-ci.org/).\n\n<p align=\"center\">\n  <img src=\"assets/demo.gif\" alt=\"mcp-zuul diagnosing a build failure\" width=\"800\" />\n</p>\n\n> If mcp-zuul saves you a debugging session, a ⭐ [star](https://github.com/imatza-rh/mcp-zuul) helps others find it.\n\n**One command, no install:**\n```bash\nclaude mcp add zuul -e ZUUL_URL=https://your-zuul.example.com -- uvx mcp-zuul\n```\n\n48 tools, 5 prompts, 3 resources — covering builds, logs, pipelines, jobs, infrastructure, and live status. Works with Claude Code, Claude Desktop, Cursor, Codex, Windsurf, and any MCP-compatible client.\n\n## Why mcp-zuul?\n\n| | mcp-zuul | Raw Zuul API | Zuul web UI |\n|---|---|---|---|\n| **Failure analysis** | Structured — task, host, error, rc | Raw JSON, parse yourself | Click through log pages |\n| **Log search** | Regex + context lines + line ranges | Not available | Browser Ctrl+F |\n| **Flaky detection** | Automatic pass/fail statistics | Manual query + calculate | Not available |\n| **Test results** | Parsed JUnit XML with failure details | Not available | External link |\n| **Anomaly detection** | ML-based via LogJuicer | Not available | Not available |\n| **Live status** | Job progress, ETA, pre-failure alerts | Polling API | Manual refresh |\n| **Multi-instance** | One config entry per Zuul | Different base URLs | Different browser tabs |\n\n## Quick Start\n\n**uvx** (no install, recommended):\n```bash\nclaude mcp add zuul \\\n               -e ZUUL_URL=https://softwarefactory-project.io/zuul \\\n               -e ZUUL_DEFAULT_TENANT=rdoproject.org \\\n               -- uvx mcp-zuul\n```\n\n**pip**:\n```bash\npip install mcp-zuul\n```\n\n**Docker**:\n```bash\ndocker build -t mcp-zuul .\n```\n\n**[LobeHub](https://lobehub.com/mcp/imatza-rh-mcp-zuul)** — send this to your AI agent:\n```\nRead https://lobehub.com/mcp/imatza-rh-mcp-zuul/skill.md and follow the instructions to install the MCP server.\n```\n\nSee [Setup](#setup) for full configuration options including Kerberos and multi-instance.\n\n## Features\n\n**Structured failure analysis** — `get_build_failures` parses Zuul's `job-output.json` and returns exactly which Ansible task failed, on which host, with error message, return code, and stderr. No log scrolling needed.\n\n**Read any log file** — `get_build_log` isn't limited to `job-output.txt`. Pass `log_name` to read any file in the build's log directory (ci_script logs, ansible.log, deployment logs) with full grep, tail, and line-range support.\n\n**Precise log navigation** — Jump to exact line ranges with `start_line`/`end_line`. After finding an error at line 6148, read lines 6130-6160 instead of scrolling through 200-line chunks.\n\n**Smart grep** — Regex search with context lines. Auto-converts common shell-grep `\\|` syntax to Python regex `|` so patterns like `error\\|failed\\|timeout` just work.\n\n**Live pipeline awareness** — `get_change_status` returns live job progress with elapsed times, estimated completion, and pre-failure detection (`pre_fail` field). When the change isn't in pipeline, automatically fetches the latest completed buildset.\n\n**Tool filtering** — Reduce LLM tool-selection noise with `ZUUL_ENABLED_TOOLS` or `ZUUL_DISABLED_TOOLS`. Only expose the tools your workflow needs — the rest are removed from the server entirely.\n\n**URL-based input** — Paste a Zuul build URL directly. Tools auto-parse the tenant and UUID from URLs like `https://zuul.example.com/t/tenant/build/abc123` — no manual extraction needed.\n\n**Flaky job detection** — `find_flaky_jobs` analyzes recent build history and computes pass/fail statistics to identify intermittent failures automatically.\n\n**Job dependency graph** — `get_freeze_jobs` returns the fully-resolved job graph for a pipeline/project/branch, showing all jobs with their dependencies after inheritance resolution.\n\n**Kerberos/SPNEGO auth** — First-class support for Zuul instances behind OIDC + Kerberos. Drives the full SPNEGO redirect chain automatically. Session cookies persist and re-authenticate transparently on expiry.\n\n**Streamable HTTP transport** — Run as a persistent HTTP server with `MCP_TRANSPORT=streamable-http` for remote/shared deployment. Supports stdio (default), SSE, and streamable-http.\n\n**Write operations** — Enqueue/dequeue/promote changes and re-enqueue buildsets. Pipeline-affecting tools are disabled by default (`ZUUL_READ_ONLY=true`) and removed from the server entirely so LLMs don't even see them. Autohold management (create/delete) is always available since it doesn't affect running pipelines.\n\n**LogJuicer integration** — `get_build_anomalies` uses ML-based log analysis to find unusual lines by comparing failed logs against successful baselines. Optional — requires `LOGJUICER_URL`.\n\n**Token-efficient output** — All responses strip None values and use compact formatters. `tail_build_log` returns just the last N lines — the fastest way to check why a build failed.\n\n**Error handling** — All tools return JSON, errors included. Network failures, auth issues, and invalid parameters produce `{\"error\": \"descriptive message\"}`. Tools never raise unhandled exceptions.\n\n## Tools\n\n### Builds & Failures\n\n| Tool | What it does |\n|------|-------------|\n| `list_builds` | Search builds by project, pipeline, job, change, result. Includes `buildset_uuid` for cross-referencing. |\n| `get_build` | Full build details — nodeset, log URL, artifacts, error detail. Accepts `url` or `uuid`. |\n| `get_build_failures` | **Start here for failures.** Structured task-level data from `job-output.json` — failed play, task, host, msg, rc, stderr/stdout. Accepts `url` or `uuid`. |\n| `diagnose_build` | **One-call failure diagnosis.** Combines structured failures from `job-output.json` with targeted log context (fatal/FAILED lines with surrounding context from `job-output.txt`). Use instead of calling `get_build_failures` + `get_build_log` separately. Accepts `url` or `uuid`. |\n| `get_build_log` | Read and search log files. Modes: `summary` (tail + error lines), `full` (paginated), `grep` (regex + context), `start_line`/`end_line` (exact range). Supports `log_name` for any file. Accepts `url` or `uuid`. |\n| `tail_build_log` | **Fastest failure check.** Last N lines of a log (default 50, max 500). More token-efficient than `get_build_log` summary mode. Accepts `url` or `uuid`. |\n| `browse_build_logs` | List log directory contents or fetch specific files (inventory, artifacts, must-gather). Max 512KB per file. Accepts `url` or `uuid`. |\n| `stream_build_console` | **Live console from RUNNING builds.** Connects to Zuul WebSocket, returns last N lines (tail). For completed builds, use `tail_build_log`. Optional — requires `pip install mcp-zuul[console]`. |\n\n### Buildsets\n\n| Tool | What it does |\n|------|-------------|\n| `list_buildsets` | Search buildsets. Use `include_builds=true` to inline full build details (saves round-trips). |\n| `get_buildset` | Full buildset with all builds and events. Accepts `url` or `uuid`. |\n\n### Pipeline & Status\n\n| Tool | What it does |\n|------|-------------|\n| `get_status` | Live pipeline status — what's queued, running, with job progress and ETA. Filterable by pipeline and project. |\n| `get_change_status` | Status for a change/PR/MR. In pipeline: live jobs with elapsed times. Not in pipeline: auto-fetches latest completed buildset. Accepts `url` or `change`. |\n| `list_pipelines` | All pipelines with their trigger types. |\n\n### Jobs & Projects\n\n| Tool | What it does |\n|------|-------------|\n| `list_tenants` | All tenants with project counts. |\n| `list_jobs` | List jobs with optional name filter. |\n| `get_job` | Job configuration — parent, nodeset, timeout, variants, source project. |\n| `get_project` | Which pipelines and jobs are configured for a project. |\n| `list_projects` | List all projects in a tenant with optional name filter. |\n| `get_config_errors` | **Check this when jobs aren't running.** Configuration errors, missing refs, broken configs. Filterable by project. |\n| `get_freeze_jobs` | Resolved job dependency graph for a pipeline/project/branch. Shows exactly which jobs will run with inheritance resolved. |\n| `get_freeze_job` | **Resolved job config after inheritance.** Final merged nodeset, playbooks, variables, and timeout for a specific job. Answers \"what will this job actually do?\" |\n| `find_flaky_jobs` | Analyze recent build history for intermittent failures. Computes pass/fail rate and flags jobs as flaky (>20% failure with mixed results). |\n| `get_build_times` | Build duration trends with avg/min/max stats. Detect performance regressions or timeout-prone jobs. |\n| `get_job_durations` | Batch avg/min/max duration for multiple jobs in one call. Designed for monitoring an entire pipeline chain without N separate calls. |\n| `check_health` | Test API connectivity, auth status, and config. Triggers re-auth automatically if the Kerberos session expired. |\n| `get_tenant_info` | Tenant capabilities — auth realms, job history support, websocket URL. |\n\n### Infrastructure\n\n| Tool | What it does |\n|------|-------------|\n| `list_nodes` | Nodepool nodes with state (ready, in-use, building), provider, and label. Includes state summary. |\n| `list_labels` | Available nodepool labels — what node types jobs can request. |\n| `list_semaphores` | Resource locks with current holders and max capacity. Check when jobs wait unexpectedly. |\n| `list_autoholds` | Active autohold requests — nodes held after failure for debugging. |\n| `get_autohold` | Full details of a specific autohold request — held nodes, timing, project/job. |\n| `list_providers` | Nodepool cloud providers with flavors (VM sizes), images, and labels. |\n| `list_images` | Nodepool disk images with build status and provider upload state. |\n| `list_system_events` | System events — config updates, reconfigurations, pipeline changes. Useful for \"why did my job stop running?\" |\n| `get_badge` | CI status badge URL (SVG) for a project — embeddable in READMEs with Markdown snippet. |\n| `get_connections` | Configured source connections — Gerrit, GitHub, GitLab instances with driver and hostname. |\n| `get_components` | System components — schedulers, executors, mergers, web servers with state and version. |\n\n### Write Operations\n\nPipeline-affecting operations — disabled by default (`ZUUL_READ_ONLY=true`). Set `ZUUL_READ_ONLY=false` to enable. Requires auth token or Kerberos. Autohold management (create/delete) is always available since it doesn't affect running pipelines.\n\n| Tool | What it does |\n|------|-------------|\n| `enqueue` | Enqueue a change or ref into a pipeline. Supports both change-based (check/gate) and ref-based (periodic) enqueue. |\n| `promote` | Promote changes to the top of a pipeline queue. Use for urgent fixes when gate has a long queue. |\n| `reenqueue_buildset` | Re-enqueue a buildset — reads project/pipeline/ref from a previous buildset and enqueues it again. |\n| `dequeue` | Remove a change or ref from a pipeline. **Destructive.** |\n| `autohold_create` | Create an autohold request — hold nodes after failure for debugging. Not gated by `ZUUL_READ_ONLY`. |\n| `autohold_delete` | Delete an autohold request. Not gated by `ZUUL_READ_ONLY`. |\n\n### Test Results & Log Analysis\n\n| Tool | What it does |\n|------|-------------|\n| `get_build_test_results` | **Parse JUnit XML test results.** Discovers test files via `zuul-manifest.json`, returns structured pass/fail/skip counts with failure details. Works with tempest, tobiko, and any JUnit XML output. |\n| `get_build_anomalies` | ML-based log anomaly detection via [LogJuicer](https://github.com/logjuicer/logjuicer). Compares failed logs against successful baselines. Requires `LOGJUICER_URL`. |\n\n## Prompts\n\nPre-built prompt templates that pre-load context and guide analysis:\n\n| Prompt | What it does |\n|--------|-------------|\n| `debug_build` | Fetches build details + structured failures, checks for flaky signal from recent history, then guides root cause analysis. |\n| `compare_builds` | Loads two builds side-by-side with inline failure data for differential analysis — \"why did this start failing?\" |\n| `check_change` | Determines live pipeline status or latest results for a change, with appropriate next steps. |\n| `tenant_health` | Assesses overall tenant health — components, config errors, and node pool status in one view. |\n| `diagnose_queue_delay` | Diagnoses why jobs are queued or delayed — checks nodes, semaphores, and system state. |\n\n## Resources\n\nBrowsable context that clients can attach to conversations without tool calls:\n\n| Resource | URI Pattern |\n|----------|-------------|\n| Build details | `zuul://{tenant}/build/{uuid}` |\n| Job configuration | `zuul://{tenant}/job/{name}` |\n| Project configuration | `zuul://{tenant}/project/{org}/{repo}` |\n\n## Setup\n\n### MCP client configuration\n\nAll clients use the same JSON structure. Add to your client's MCP config file:\n\n**Claude Code** (`~/.claude.json` → `mcpServers`):\n```json\n{\n  \"mcpServers\": {\n    \"zuul\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-zuul\"],\n      \"env\": {\n        \"ZUUL_URL\": \"https://softwarefactory-project.io/zuul\",\n        \"ZUUL_DEFAULT_TENANT\": \"rdoproject.org\"\n      }\n    }\n  }\n}\n```\n\n**Claude Desktop** (`claude_desktop_config.json`), **Cursor** (`.cursor/mcp.json`), and other MCP clients use the same format. GUI-based clients don't inherit your shell `PATH` - use the full path to `uvx` (run `which uvx` to find it).\n\nOr via CLI:\n```bash\nclaude mcp add zuul \\\n               -e ZUUL_URL=https://softwarefactory-project.io/zuul \\\n               -e ZUUL_DEFAULT_TENANT=rdoproject.org \\\n               -- uvx mcp-zuul\n```\n\n### Environment variables\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `ZUUL_URL` | Yes | — | Zuul base URL (e.g. `https://softwarefactory-project.io/zuul`) |\n| `ZUUL_DEFAULT_TENANT` | No | — | Default tenant (saves passing `tenant` on every call) |\n| `ZUUL_AUTH_TOKEN` | No | — | Bearer token for authenticated instances |\n| `ZUUL_USE_KERBEROS` | No | `false` | Enable Kerberos/SPNEGO authentication |\n| `ZUUL_TIMEOUT` | No | `30` | HTTP timeout in seconds |\n| `ZUUL_VERIFY_SSL` | No | `true` | SSL certificate verification |\n| `MCP_TRANSPORT` | No | `stdio` | Transport: `stdio`, `sse`, or `streamable-http` |\n| `MCP_HOST` | No | `127.0.0.1` | HTTP server bind address (non-stdio transports) |\n| `MCP_PORT` | No | `8000` | HTTP server port (non-stdio transports) |\n| `ZUUL_ENABLED_TOOLS` | No | — | Comma-separated list of tools to enable (disables all others) |\n| `ZUUL_DISABLED_TOOLS` | No | — | Comma-separated list of tools to disable (mutually exclusive with above) |\n| `ZUUL_READ_ONLY` | No | `true` | Set to `false` to enable pipeline-affecting write operations (enqueue, promote, dequeue, reenqueue_buildset). Autohold management (create/delete) is always available. |\n| `LOGJUICER_URL` | No | — | LogJuicer base URL for ML-based log anomaly detection |\n\n### Token authentication\n\nPass `ZUUL_AUTH_TOKEN` via host environment — **never hardcode tokens in config files** (visible in `ps` output):\n\n```bash\nexport ZUUL_AUTH_TOKEN=<your-token>\n```\n\nFor Docker, forward without a value to inherit from host:\n```json\n\"args\": [\"run\", \"-i\", \"--rm\", \"-e\", \"ZUUL_AUTH_TOKEN\", \"mcp-zuul\"]\n```\n\n### Kerberos / SPNEGO\n\nFor Zuul behind OIDC + Kerberos. Requires a valid Kerberos ticket (`kinit`) and the `gssapi` package.\n\n**Linux prerequisites** - `gssapi` has no pre-built Linux wheels and must compile from source:\n```bash\n# Fedora/RHEL/CentOS\nsudo dnf install krb5-devel python3-devel gcc\n\n# Debian/Ubuntu\nsudo apt install libkrb5-dev python3-dev gcc\n```\n\nmacOS and Windows have pre-built wheels - no extra packages needed.\n\nThen install with Kerberos support:\n```bash\npip install mcp-zuul[kerberos]    # or: uvx --with \"mcp-zuul[kerberos]\" mcp-zuul\n```\n\nVia CLI:\n```bash\nclaude mcp add -s user zuul-internal \\\n               -e ZUUL_URL=https://internal-zuul.example.com/zuul \\\n               -e ZUUL_DEFAULT_TENANT=my-tenant \\\n               -e ZUUL_USE_KERBEROS=true \\\n               -e ZUUL_VERIFY_SSL=false \\\n               -- uvx --with \"mcp-zuul[kerberos]\" mcp-zuul\n```\n\nOr via JSON config:\n```json\n{\n  \"zuul-internal\": {\n    \"command\": \"uvx\",\n    \"args\": [\"--with\", \"mcp-zuul[kerberos]\", \"mcp-zuul\"],\n    \"env\": {\n      \"ZUUL_URL\": \"https://internal-zuul.example.com/zuul\",\n      \"ZUUL_USE_KERBEROS\": \"true\",\n      \"ZUUL_VERIFY_SSL\": \"false\"\n    }\n  }\n}\n```\n\nFor Docker, mount the Kerberos ticket cache:\n```bash\ndocker run -i --rm \\\n  -v /etc/krb5.conf:/etc/krb5.conf:ro \\\n  -v /tmp/krb5cc_$(id -u):/tmp/krb5cc_$(id -u):ro \\\n  -e KRB5CCNAME=/tmp/krb5cc_$(id -u) \\\n  -e ZUUL_URL=https://internal-zuul.example.com/zuul \\\n  -e ZUUL_USE_KERBEROS=true \\\n  mcp-zuul\n```\n\n### Multiple instances\n\nAdd separate entries per Zuul instance:\n```json\n{\n  \"mcpServers\": {\n    \"zuul-rdo\": {\n      \"command\": \"uvx\", \"args\": [\"mcp-zuul\"],\n      \"env\": { \"ZUUL_URL\": \"https://softwarefactory-project.io/zuul\", \"ZUUL_DEFAULT_TENANT\": \"rdoproject.org\" }\n    },\n    \"zuul-internal\": {\n      \"command\": \"mcp-zuul\",\n      \"env\": { \"ZUUL_URL\": \"https://internal.example.com/zuul\", \"ZUUL_USE_KERBEROS\": \"true\" }\n    }\n  }\n}\n```\n\n## Troubleshooting\n\n**`krb5-config: not found` or `Python.h: No such file`** when installing `mcp-zuul[kerberos]` on Linux:\n\n`gssapi` has no pre-built Linux wheels - it compiles from source. Install system packages first:\n```bash\n# Fedora/RHEL/CentOS\nsudo dnf install krb5-devel python3-devel gcc\n\n# Debian/Ubuntu\nsudo apt install libkrb5-dev python3-dev gcc\n```\n\n**`uvx: command not found`** in Cursor or Claude Desktop:\n\nGUI-based MCP clients don't inherit your shell `PATH`. Use the full path to `uvx`:\n```bash\nwhich uvx    # find the path, e.g. /usr/bin/uvx or ~/.local/bin/uvx\n```\nThen use that absolute path as `command` in your MCP config:\n```json\n\"command\": \"/usr/bin/uvx\"\n```\n\n**Permission errors** on `~/.local/share/uv/`:\n\nIf `uv` was previously run with `sudo`, the cache directory may be root-owned:\n```bash\nsudo chown -R $(whoami) ~/.local/share/uv/\n```\n\n## Usage Examples\n\n### Debug a build failure\n\n```\n\"Why did the latest build of my-project fail?\"\n```\n→ `list_builds(project=\"my-project\", result=\"FAILURE\", limit=1)` → `get_build_failures(uuid=\"...\")` → root cause with task name, error, and return code.\n\n### Deep-dive into logs\n\n```\n\"The structured data says 'non-zero return code' but no error detail.\n Check the ci_script logs.\"\n```\n→ `browse_build_logs(uuid=\"...\", path=\"controller/ci-framework-data/logs/\")` → finds `ci_script_008_run.log` → `get_build_log(uuid=\"...\", log_name=\"controller/ci-framework-data/logs/ci_script_008_run.log\", grep=\"error|timed out|Error 1\", context=2)` → exact error with surrounding context.\n\n### Navigate to a specific error\n\n```\n\"Show me lines 6478-6484 of the job output\"\n```\n→ `get_build_log(uuid=\"...\", start_line=6478, end_line=6484)` → exactly those 7 lines.\n\n### Check live pipeline status\n\n```\n\"Is change 54321 in any pipeline?\"\n```\n→ `get_change_status(change=\"54321\")` → live jobs with elapsed times and ETA, or latest completed buildset if not in pipeline.\n\n### Compare build results across a pipeline\n\n```\n\"Show me all builds from the latest buildset\"\n```\n→ `list_builds` to get `buildset_uuid` → `get_buildset(uuid=\"...\")` → all sibling builds with results and durations.\n\n### Paste a Zuul URL directly\n\n```\n\"What went wrong with this build?\n https://zuul.example.com/t/tenant/build/abc123def\"\n```\n→ `get_build_failures(url=\"https://zuul.example.com/t/tenant/build/abc123def\")` → tenant and UUID auto-extracted.\n\n### Debug why a job isn't running\n\n```\n\"My project's check pipeline seems broken — jobs aren't triggering\"\n```\n→ `get_config_errors(project=\"org/my-project\")` → configuration errors, missing refs, or repo access issues.\n\n### Check node availability\n\n```\n\"Jobs are stuck in queue — are there nodes available?\"\n```\n→ `list_nodes()` → node states with by_state summary → `list_labels()` → available node types.\n\n### Detect flaky jobs\n\n```\n\"Is this job flaky? It keeps failing intermittently\"\n```\n→ `find_flaky_jobs(job_name=\"my-deploy-job\", limit=30)` → pass/fail stats, failure rate, flaky=true/false.\n\n### See what jobs run for a project\n\n```\n\"What jobs are configured for openstack-operator in the check pipeline?\"\n```\n→ `get_freeze_jobs(pipeline=\"check\", project=\"openstack-k8s-operators/openstack-operator\")` → resolved job graph with dependencies.\n\n### Quick log tail\n\n```\n\"Show me the last 30 lines of the build log\"\n```\n→ `tail_build_log(uuid=\"...\", lines=30)` → just the tail, minimal tokens.\n\n### What nodeset does my job use after inheritance?\n\n```\n\"What nodeset and playbooks will deploy-job actually use?\"\n```\n→ `get_freeze_job(pipeline=\"check\", project=\"org/repo\", job_name=\"deploy-job\")` → resolved nodeset, playbooks, variables, timeout after all parent inheritance.\n\n## Development\n\n```bash\ngit clone https://github.com/imatza-rh/mcp-zuul.git\ncd mcp-zuul\nuv sync --extra dev\n\n# Run locally\nZUUL_URL=https://softwarefactory-project.io/zuul uv run mcp-zuul\n\n# Run tests\nuv run pytest tests/ -v\n\n# Lint and format\nuv run ruff check src/ tests/\nuv run ruff format --check src/ tests/\n\n# Type check\nuv run mypy src/mcp_zuul/\n\n# Build Docker image\ndocker build -t mcp-zuul .\n```\n\n### Architecture\n\n```\nMCP Client (Claude Code, Cursor, etc.)\n    │\n    ▼\n┌──────────────────────────────────────────────────┐\n│  src/mcp_zuul/                                   │\n│                                                  │\n│  server.py       MCPServer instance               │\n│  config.py       env vars, transport, filtering  │\n│  auth.py         Kerberos/SPNEGO + OIDC          │\n│  errors.py       @handle_errors decorator        │\n├──────────────────────────────────────────────────┤\n│  tools/          48 tools across 8 submodules    │\n│  prompts.py      5 prompt templates              │\n│  resources.py    3 zuul:// resources             │\n├──────────────────────────────────────────────────┤\n│  helpers.py      API client, URL parsing         │\n│  formatters.py   token-efficient output          │\n│  parsers.py      Ansible/JUnit/log parsing       │\n│  classifier.py   failure classification          │\n├──────────────────────────────────────────────────┤\n│  httpx clients   API (auth) + logs (no auth)     │\n└────────┬─────────────────────────┬───────────────┘\n         ▼                         ▼\n    Zuul REST API           Log file hosts\n```\n\nSee `CLAUDE.md` for full architecture details.\n\n## Listings\n\n<a href=\"https://glama.ai/mcp/servers/imatza-rh/mcp-zuul\">\n  <img width=\"380\" height=\"200\" src=\"https://glama.ai/mcp/servers/imatza-rh/mcp-zuul/badge\" alt=\"mcp-zuul MCP server\" />\n</a>\n\n- [PyPI](https://pypi.org/project/mcp-zuul/) — `pip install mcp-zuul`\n- [Glama](https://glama.ai/mcp/servers/imatza-rh/mcp-zuul) — Quality: A/A/A\n- [Conare](https://conare.ai/marketplace/mcp/mcp-zuul) — Marketplace listing\n- [PulseMCP](https://www.pulsemcp.com/servers?q=zuul) — Server directory\n- [LobeHub](https://lobehub.com/mcp/imatza-rh-mcp-zuul) — MCP marketplace with agent install\n- [HiFriendBot](https://hifriendbot.com/ai-list/zuul-ci-by-imatza-rh/) — AI tools directory\n- [pepy.tech](https://pepy.tech/projects/mcp-zuul) — Download statistics\n- [Docker](https://github.com/imatza-rh/mcp-zuul/pkgs/container/mcp-zuul) — `ghcr.io/imatza-rh/mcp-zuul`\n\n## Contributing\n\nContributions welcome. Please open an issue first to discuss significant changes.\n\n```bash\n# Fork, clone, and install dev dependencies\nuv sync --extra dev\n\n# Make changes, then verify\nuv run pytest tests/ -v\nuv run ruff check src/ tests/\nuv run ruff format src/ tests/\nuv run mypy src/mcp_zuul/\n```\n\n## License\n\nApache-2.0\n",
  "bytes": 24957,
  "sha": "79efcff4d00e9076da22bb02a9cd1d1f43dddd6308695cedd94b6fee3e71e259",
  "repo_slug": "imatza-rh/mcp-zuul",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_imatza_rh_mcp_zuul_0974603d/readme"
}