使用说明
helix.mcp
An increasingly inaccurately named CLI and MCP server for AI agents investigating .NET CI/CD failures across Helix and Azure DevOps.
No MCP needed.
hlxworks as a standalone CLI —dnx lewing.helix.mcp <command>(zero-install) ordotnet tool install -g lewing.helix.mcp. See the CLI reference.
Built with Squad — meet the squad.
Why?
AI agents investigating CI failures in dotnet repos (runtime, sdk, aspnetcore, etc.) hit two problems:
- Raw APIs return too much data. A single Helix console log can be megabytes. The Azure DevOps build timeline returns hundreds of records. Dumping this into a context window wastes tokens and drowns the signal.
- Every agent starts from scratch. When multiple agents — or the same agent across tool calls — inspect the same build, each one repeats the same API calls and downloads.
hlx solves both:
- Return less, return better. Tools like
helix_searchandazdo_search_logsearch in place and return only matching lines with context — agents never download a full log.helix_statusreturns structured failure summaries instead of raw JSON. Defaulttaillimits (500 lines),filterparameters (failedby default), andmaxMatchescaps keep responses focused. - Cache everything, share across processes. A local SQLite cache sits between agents and the APIs. Different MCP server instances (one per IDE window/terminal) share the same cache, so the second agent to inspect a job gets instant results. Smart TTLs track job lifecycle — running jobs cache briefly (15–30s), completed jobs cache for hours.
Zero config — public dotnet CI works out of the box. Install and go.
Context-Efficient Design
Every tool is designed to minimize token consumption in agent context windows:
| Technique | How it helps |
|---|---|
| Tail limits | helix_logs and azdo_log return the last N lines (default 500), not the full log |
| Complete collection on demand | CLI list commands such as azdo test-runs --all and azdo test-results --all populate complete-list cache keys for deterministic scanner snapshots while MCP defaults stay capped |
| Pattern search | helix_search and azdo_search_log search outside agent context and return matching lines with configurable context — no full ingestion |
| Failure-first defaults | helix_status, azdo_timeline, azdo_test_results default to showing only failures |
| Structured JSON | Failure summaries, test results, and timeline data come pre-parsed — no agent-side text extraction |
| Batch operations | helix_batch_status checks up to 50 jobs in one call; helix_find_files scans N work items instead of N+1 API calls |
| Ranked search | azdo_search_log can search all build logs, ranking by failure likelihood and stopping early when maxMatches is reached |
| Idempotent annotations | Read-only tools marked safe for retry and caching — clients can optimize scheduling and error recovery |
Investigation Path
- When repo workflows vary, start with
helix_ci_guide(repo)for the repo-specific path and search patterns. - Use
helix_parse_uploaded_trxonly when the work item uploads structured test results to Helix (runtime CoreCLR, XHarness device tests). - Otherwise use
azdo_test_runs→azdo_test_resultsfor structured results, orhelix_searchwhen the signal is in console output.
Artifact bytes, binlogs, and attachments
azdo_artifacts and azdo_test_attachments list metadata. Use
azdo_download_artifacts / azdo_download_test_attachments (MCP), or:
hlx azdo download-artifacts 1621192 \
--artifact-name Logs_Build_Attempt1_browser_wasm_linux_Release_LibraryTestsCoreCLR \
--member-path Release/Build.binlog --output-dir ./evidence --json
hlx azdo download-test-attachments 98765 --result-id 1234 \
--pattern '*.dmp' --output-dir ./evidence --json
hlx azdo download-test-attachments 98765 --all --delivery file --json
Exact artifact member paths use native file transfer (including PipelineArtifact's
leading-slash subPath); member globs spool the archive to private disk and retain
only matched decoded files and a covering catalog. Without member selectors, the
requested original archive/binary is preserved. **/Build.binlog also matches root
members; */Build.binlog requires a directory.
Explicit selectors are not restricted to failed Logs or a content classification.
There is no inferred 50/512 MiB per-object ceiling. Optional caller planning bounds
can be waived with --allow-large; physical storage, authentication, unsafe paths,
and integrity checks remain real protections. Progress goes to stderr; JSON contains
SHA-256, server-local paths, bounded report windows, and a verified full-report
delivery reference. Continue through hlx_read_evidence, or
hlx evidence read <evidence-id> --encoding base64 --offset-bytes 0 --length-bytes 2048.
Use CLI on the debugger host or a deliberately shared filesystem for binaries:
there is no public transfer endpoint, automatic upload, dump debugging, or binlog
event parsing.
Collection stays metadata-only unless byte flags are supplied:
--download-azdo-artifacts <glob>, --download-artifact-members <glob>, and
--download-test-attachments <glob>. Explicit artifact-name globs select that set,
not an implicit failed-only subset. Automatic --artifact-byte-scope failed-logs
prefers failed-leg Logs/Build.binlog. Unattended retained/streamed byte planning
defaults to 2 GiB; --allow-large resumes the original scope. Verified files survive
retries; interrupted upstream files restart without Range, while local evidence
reads always support exact byte offsets. Selected byte/cache descriptor pairs export
in the existing cache snapshot; metadata-only older snapshots cannot supply missing
bytes. Recollect in live mode into a new snapshot rather than modifying the old one.
Current scope is not full collector-design compliance: broad test-result and attachment-volume recovery repairs remain follow-up work. ZIP bodies spool to disk, but member catalogs and full download-report serialization still materialize in memory; bounded inline windows do not imply bounded-memory acquisition.
Cross-Process Caching
IDE / Agent processes hlx stdio MCP servers
┌─────────────┐ ┌──────────────┐
│ VS Code │──────────────▶│ hlx pid 1 │──┐
│ Copilot CLI│──────────────▶│ hlx pid 2 │──┤
│ Other agent│──────────────▶│ hlx pid 3 │──┤
└─────────────┘ └──────────────┘ │
▼
┌─────────────┐ miss ┌─────────────────┐
│ Cache Layer │──────────▶│ Helix / AzDO API│
│ Cache hit? │◀──────────│ │
└──────┬───────┘ response └─────────────────┘
hit │
▼
┌───────────────────────┐
│ SQLite + Disk shared │
│ ┌─────────┬────────┐ │
│ │SQLite DB │Artifact│ │
│ │WAL mode │ files │ │
│ └─────────┴────────┘ │
│ │
│ Auth isolation: │
│ cache-a1b2c3/ │
│ cache-d4e5f6/ │
│ public/ │
└───────────────────────┘
- SQLite WAL mode — multiple processes read/write the same DB safely with busy timeout
- Smart TTLs — running jobs: 15–30s, completed jobs: 1–4h, console logs for running jobs: never cached. AzDO in-progress builds use dual-key freshness with incremental log fetching (delta-append)
- Auth-isolated storage — each unique token gets its own cache directory (
cache-{hash}/). Unauthenticated requests usepublic/. No cross-token leakage - LRU eviction — 1 GB cap (configurable via
HLX_CACHE_MAX_SIZE_MB), artifact files expire after 7 days without access
| Setting | Default | Env var |
|---|---|---|
| Max cache size | 1 GB | HLX_CACHE_MAX_SIZE_MB (set to 0 to disable) |
| Cache location (Windows) | %LOCALAPPDATA%\hlx\ |
— |
| Cache location (Linux/macOS) | $XDG_CACHE_HOME/hlx/ |
— |
hlx cache status # Show cache size, entry count, oldest/newest entries
hlx cache clear # Wipe all cached data
Delivered evidence retention
Oversized MCP responses delivered as files live inside the existing per-user cache trust boundary:
<effective-cache-root>/delivery/<credential-partition>/. The effective root already isolates
live auth contexts. Offline replay never writes into its immutable snapshot: delivery goes to
<normal-user-cache-root>/eval-delivery/<SHA256-of-normalized-snapshot-path>/<credential-partition>/.
The normal user root is %LOCALAPPDATA%/hlx on Windows or $XDG_CACHE_HOME/hlx
(default ~/.cache/hlx) on Unix; XDG_RUNTIME_DIR and HLX_DELIVERY_RUNTIME_DIR are not used.
If that root overlaps the snapshot in either direction or delivery cannot write there, replay
uses <LocalApplicationData>/hlx/delivery-ephemeral/<snapshot-hash>/<credential-partition>/,
falling back to ~/.hlx/delivery-ephemeral/<snapshot-hash>/<credential-partition>/ if needed.
Fallbacks must be writable and outside the snapshot. Overlap checks normalize full paths,
resolve existing directory symlinks, and ignore case on Windows/macOS.
Each delivery partition has a separate soft sub-budget of 25% of HLX_CACHE_MAX_SIZE_MB
(minimum one byte), tracked in delivery.db rather than included in hlx cache status artifact
bytes. Every put, dedupe hit, descriptor lookup, and read renews a 24-hour pin. Expired unpinned
entries follow the cache's artifact-age limit and oldest-access-first size eviction. Pins always
win over the soft cap; FileEvidenceDeliveryStore.TryGetRetentionStatus reports pinned bytes and
any overage. SQLite transactions serialize pins and eviction across processes.
With HLX_CACHE_MAX_SIZE_MB=0, file delivery still works under
<base-cache-root>/delivery-ephemeral/<credential-partition>/, with the same pins and retention
and a 256 MiB soft budget. An unwritable root uses the same external per-user fallback folders
(without a snapshot hash). Delivery is reconstructable evidence, not part of snapshot exports;
hlx cache clear clears the primary cache, not delivery evidence protected by these pins. Delivery
follows the cache's trust model without separate owner/ACL checks. New Unix directories/files are
created with modes 0700/0600 as defense in depth.
Offline Snapshots
Export the current cache as a portable snapshot for offline replay, testing, or reproducible scenarios:
hlx snapshot export /tmp/my-snapshot # Create snapshot
hlx snapshot validate /tmp/my-snapshot # Verify integrity
HLX_EVAL_SNAPSHOT=/tmp/my-snapshot hlx status <jobId> # Use snapshot
Snapshots preserve all cached data and artifact files. When HLX_EVAL_SNAPSHOT is set, hlx runs entirely offline against the snapshot's SQLite database, including the non-secret AzDO cache partition recorded during collection; replay does not need AZDO_TOKEN or az login. For scanner bundles, prefer collecting in CI with hlx collect azdo-build --export, upload the snapshot, and investigate anywhere from the complete keys populated during collection. See the CLI reference for partition selection and the complete workflow.
Schema v2 snapshots also preserve deterministic acquisition failures (not_found, access_denied, invalid_response) recorded during live collection. Offline replay returns the original provider failure with source: "snapshot", replayed: true, and recordedAt; keys never collected into the snapshot return kind: "not_in_snapshot", provider: "cache".
Snapshots produced by v0.10.3 or earlier can contain empty/corrupt raw AzDO logs from NUL-containing cache values; run hlx cache clear, re-collect the snapshot, and verify with hlx snapshot validate.
Collecting scanner snapshots
Use hlx collect azdo-build to populate a deterministic cache, export it, and write a manifest in one step:
hlx collect azdo-build "$BUILD_ID_OR_URL" --export /tmp/my-snapshot
HLX_EVAL_SNAPSHOT=/tmp/my-snapshot hlx mcp
The exported manifest lives at manifest/hlx-collect-manifest.json; read it to distinguish complete snapshots from declared gaps before handing the snapshot to an offline scanner. See the scanner workflow and collect command reference.
Explicit --test-scope all streams every supported result outcome through cache-backed pages, including builds above 10,000 results. It does not request passed-result attachments: diagnostic attachment selection remains the default. Explicit --test-attachment-scope all is independently honored without a numeric unlock. Unattended attachment fan-out can defer with an exact same-request --allow-large --resume command. Progress and acquisition heartbeats go to stderr; JSON stdout stays parseable.
Query-backed build sets
hlx azdo builds / azdo_builds discover builds by pipeline exact name/glob (duplicate names form a union), build number/glob, required tags, result/reason, repository, PR/branch and queue/start/finish time. Full fields and the interactive top=20 default are preserved; --projection compact is opt-in. CLI JSON adds source completeness, exact counts when known, cache provenance and stable viewId continuation. Complete sets require --all (MCP all=true); --output, file delivery and hlx_read_evidence are optional ways to deliver them, and on their own carry only the requested page; interactive queries have no collector quota.
hlx azdo builds --definition-name '*wasm*' --result failed --projection compact --json
hlx azdo builds --pr-number 12345 --repository-id dotnet/runtime --repository-type GitHub --all --output builds.json
hlx collect azdo-builds --query build-query.json --cache-dir ./build-cache --export ./build-snapshot --json
The strict version-1 query JSON contains semantic filters, not paging/output settings. Build-set collection freezes discovery before member evidence, persists one schema-v2 manifest, then exports one existing-format snapshot. Metadata-only scope is available with --build-scope metadata. Complete query evidence replays credential-free at different limits/projections and supports provably narrower queries (for example, failed-only from an all-result PR set); uncovered scope returns not_in_snapshot with an exact live recollection/new-snapshot action.
The unattended defaults—50 builds, 25 definitions, 5,000 scanned builds, 10,000 run-wide test results and 1,000 attachment requests—are planning safeguards, not access gates. Explicit larger allowances are honored; --allow-large --resume continues the same captured query/checkpoint and waives logical thresholds, not auth, integrity or actual storage checks. See the CLI reference for query format and recovery.
Build-set resume preserves discovery checkpoints even after provider failures and reuses successful cached Helix file transfers. Result/attachment recovery commands retain the aggregate export context and select a fresh destination; existing snapshots are never overwritten.
MCP Tools
Helix Tools (9)
| Tool | Description |
|---|---|
helix_status |
Job pass/fail summary with failure categorization. Filter: failed (default), passed, all. |
helix_batch_status |
Status for up to 50 jobs at once with aggregate totals. |
helix_logs |
Console log content (last N lines, default 500). |
helix_search |
Search a console log or uploaded file for repo-specific failure patterns without downloading the full content. |
helix_files |
List uploaded files for a work item, grouped by type. |
helix_find_files |
Search across work items for files matching a glob (*.binlog, *.trx, *.dmp). |
helix_work_item |
Detailed work item info (exit code, state, machine, duration, failure category). |
helix_download |
Download files from a work item or direct blob URL. Supports glob patterns for work-item downloads. |
helix_parse_uploaded_trx |
Parse TRX/xUnit XML files uploaded to Helix blob storage into test names, outcomes, and error messages. |
AzDO Tools (12)
| Tool | Description |
|---|---|
azdo_build |
Build details (status, result, branch, timing, URL). Accepts URLs or integer IDs. |
azdo_builds |
Discover builds by pipeline name/glob, build number, tags, result/reason, repository, PR/branch and time; full/compact pages or verified file delivery. |
azdo_timeline |
Triage an AzDO build timeline: errors/Helix-first Jobs/Tasks, short deduplicated issue previews (with counts), log IDs, and ancestor context. Defaults to projection=triage (at most 10 rows, Phase rows omitted); filter defaults to failed for ordinary triage, or all once an explicit recordId/parentId/type/result/state/name selector is given with no preset. Also supports projection=compact|full|summary, paging via offset/limit/viewId/next, and maxResponseBytes (default 12288, inline shaping target — not a data-access ceiling). Full/complete selections stay reachable via delivery=file/all and hlx_read_evidence, never refused. |
azdo_log |
Log content for a specific build step (last N lines, default 500). |
azdo_search_log |
Search a specific build log or all ranked build logs for a pattern with context lines. |
azdo_search_timeline |
Search timeline records by name or issue pattern. |
azdo_changes |
Commits/changes associated with a build. |
azdo_test_runs |
Test run summaries (total, passed, failed counts). |
azdo_test_results |
Individual test results. Filter by outcome (outcomes param). Defaults to failed tests only (top 200). |
azdo_artifacts |
Build artifacts with pattern filtering (e.g., *.binlog). |
azdo_test_attachments |
Test result attachments (screenshots, logs, dumps). |
azdo_evidence_plan |
Plan failed/canceled job → artifact evidence mapping. auto (default) joins by source GUID, then falls back to normalized-exact names; source-ID-only, normalized-exact, and exact-name modes are also available. Parses selected arcade queue-monitor timeline issues into structured helixFailures[] rows (helixJobId, workItem, state, exitCode, sourceFormat, suggestedFetches). Paging fields: helixFailureOffset/Limit/Total/Truncated. Any partial Helix-failure page (helixFailureTotal > helixFailures.length, including later offset pages) is fail-closed: complete=false, truncated=true, helix_failures_truncated, and CLI exit 2. Read-only; returns ranked candidates, ambiguity and truncation metadata, completeness status, and stable incompleteDetails[].code values (never silently chooses). The MCP stripAttemptPrefix parameter defaults to true; the equivalent CLI strips by default and exposes the inverse bare flag --keep-attempt-prefix. suggestedFetches[].tool emits helix_work_item, helix_logs, and helix_files for drilldown. |
azdo_evidence_plan incomplete codes: artifact_ambiguous = multiple artifact candidates for one selected job; artifact_missing = no matching artifact candidate; candidates_truncated = a job's candidate list exceeded the per-entry bound; entries_truncated = selected jobs exceeded the plan entry bound; helix_failures_truncated = the current response contains only a partial Helix-failure page; monitor_unparseable = a selected monitor-like job had no parseable Helix work-item failures; monitor_unresolved_job_id = failure-shaped monitor entries lacked a recoverable Helix job ID. Human output reports paging ranges such as showing 1-1 of 2; collectors should treat exit 2 from paging as "fetch/merge remaining pages" or rerun with a limit at least as large as helixFailureTotal.
azdo_timeline response shaping is access-first: maxResponseBytes (default 12288) is an inline
shaping target, never a refusal — a row/record too large to inline is delivered as a verified
evidence file/reference, readable with hlx_read_evidence, instead of being dropped or erroring.
previewIssueLimit (default 5) and previewChars (default 200) bound the deduplicated issue
preview per triage row; the full, untruncated text for any shortened preview is always reachable
through the row's own recovery action. A per-call delivery="file"/all=true request retrieves
the complete selected scope, ignoring that shaping target (there is no delivery="all" value).
The CLI has an equivalent --delivery auto|inline|file|chunked flag; combine --all with
--delivery file (or just --all) for the same complete-selection behavior (--output remains
available as a CLI-specific escape hatch).
Inline maxResponseBytes is clamped to an 8 KiB–16 KiB effective range (default 12288),
independent of a smaller requested value; both the effective (clamped) maxResponseBytes and the
caller's original requestedMaxResponseBytes are reported in the response.
Parameter validation: MCP tools reject unknown parameter names with a structured error (including a "Did you mean?" hint when the unknown name is close to a known parameter) — LLM-hallucinated or mistyped param names get immediate feedback instead of silent drops. Common aliases are resolved automatically before validation:
buildId/build_id/buildUrl→buildIdOrUrlon AzDO tools that accept a build identifier;result→resultFilteronazdo_search_timeline.
Errors and exit codes
CLI JSON hard failures use { "ok": false, "error": { ... } }; MCP tool failures set isError: true and put the same object under structuredContent.error. error.kind is one of not_found, access_denied, rate_limited, timeout, transport_error, invalid_response, or not_in_snapshot; provider is azdo, helix, or cache; fields are kind, provider, operation, resource, optional httpStatus, optional retryAfterSeconds, optional snapshot replay fields (source, replayed, recordedAt), and message. Resource URL values redact query strings and fragments.
Exit codes are 0 for success (including genuinely empty successes), 1 for hard command/acquisition errors, and 2 when bounded output was written but is incomplete/truncated (for example an AzDO list JSON envelope with truncated=true, unless --allow-truncated is supplied, or an incomplete evidence plan). Callers own retry/skip policy. When a direct build-log body is empty, hlx validates the logId against the build's logs list and timeline record.log.id; if absent from both, it fails with kind=not_found. Empty full logs are not cached as successes. See the CLI reference for examples.
MCP Resources
MCP resources are URI-addressable data that clients can discover and read without invoking a tool.
| Resource URI | Description |
|---|---|
ci://profiles |
Overview of CI investigation patterns for all .NET repositories. |
ci://profiles/{repo} |
CI investigation guide for a specific .NET repository (e.g., ci://profiles/runtime). |
These provide the same CI investigation guides available via the helix_ci_guide tool, exposed as client-discoverable resources for browsing and caching.
Authentication
Helix
No auth needed for public Helix jobs (dotnet open-source CI). For private jobs:
hlx login # Opens browser, prompts for token, stores via git credential
hlx auth-status # Check current auth status
hlx logout # Remove stored token
Token resolution: HELIX_ACCESS_TOKEN env var → stored credential via git credential → error with helpful message.
Azure DevOps
The AzDO tools work anonymously against public projects (for example dnceng-public/public). For private orgs (for example devdiv/DevDiv), authentication is required.
Credential chain (tried in order):
AZDO_TOKENenvironment variable — both PATs and Entra access tokens are auto-detected:- PATs (Personal Access Tokens): sent as Basic auth. Needs Build(read) + Test(read) scopes.
- Entra/JWT tokens: sent as Bearer auth.
- Azure CLI credential (
AzureCliCredentialfromAzure.Identity) — uses youraz loginsession. Works for any org your Azure identity has access to. azCLI subprocess — falls back toaz account get-access-tokenif Azure.Identity isn't available.- Anonymous — no auth header. Works for public projects only.
When auth fails, the error tells you what was tried and how to fix it:
Can't access devdiv/DevDiv — authentication required (401).
Current auth: anonymous (no credentials found)
To resolve:
• Run 'az login' (if your Azure identity has access to this org)
• Set AZDO_TOKEN to a Personal Access Token with Build(read) + Test(read) scopes
Pass tokens via MCP config:
{
"servers": {
"hlx": {
"type": "stdio",
"command": "dotnet",
"args": ["dnx", "--yes", "lewing.helix.mcp"],
"env": {
"HELIX_ACCESS_TOKEN": "your-helix-token",
"AZDO_TOKEN": "your-azdo-pat-or-entra-token"
}
}
}
}
The HTTP MCP server supports per-request auth via Authorization: Bearer <token> headers, with isolated cache per client. Set HLX_API_KEY to gate server access.
Installation
Use with the dotnet-dnceng plugin (recommended)
The dotnet-dnceng plugin bundles hlx alongside related MCP servers (Azure DevOps, Maestro, binlog) and CI analysis skills — one install, batteries included:
copilot extensions install lewing/agent-plugins/plugins/dotnet-dnceng
This gives your agent helix_* and azdo_* tools plus skills for CI failure analysis, codeflow tracing, and dependency flow debugging.
Run with dnx (no install needed)
dnx lewing.helix.mcp
dnx (new in .NET 10) auto-downloads and runs NuGet tool packages. MCP mode is the default when no subcommand is given.
Install as global tool
dotnet tool install -g lewing.helix.mcp
After installation, hlx is available as a command. See the CLI reference for standalone usage.
Build from source
git clone https://github.com/lewing/helix.mcp.git
cd helix.mcp
dotnet build # Requires .NET 10 SDK
The
Microsoft.DotNet.Helix.Clientpackage comes from the dotnet-eng feed. The includednuget.configreferences it.
MCP Configuration
Add to your MCP client config:
{
"servers": {
"hlx": {
"type": "stdio",
"command": "dotnet",
"args": ["dnx", "--yes", "lewing.helix.mcp"]
}
}
}
If installed as a global tool, use
"command": "hlx"with"args": []instead.
| Client | Config file | Top-level key |
|---|---|---|
| VS Code / GitHub Copilot | .vscode/mcp.json |
servers |
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json |
mcpServers |
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json |
mcpServers |
| Claude Code / Cursor | .cursor/mcp.json |
mcpServers |
VS Code uses
servers. Claude Desktop, Claude Code, and Cursor usemcpServers— the rest is identical.
If MCP isn't working, the same data is available via
hlx <command>directly — see the CLI reference.
For HTTP (remote/shared servers):
{
"servers": {
"hlx": {
"type": "http",
"url": "http://localhost:3001"
}
}
}
For MCP gateways that spawn stdio MCP servers as Docker subprocesses (e.g. GitHub Agentic Workflows's MCP Gateway), use the published container image:
# Recommended (pinned release):
mcp-servers:
hlx:
container: ghcr.io/lewing/helix.mcp:v0.8.0
env:
AZURE_DEVOPS_EXT_PAT: ""
HELIX_ACCESS_TOKEN: ""
# Or, for following the latest release:
# container: ghcr.io/lewing/helix.mcp:latest
Images are published on tagged releases (v*); the publish workflow also supports workflow_dispatch for backfilling images for an existing tag or publishing ad-hoc validation tags. Multi-arch: linux/amd64, linux/arm64.
Security
- Safe XML parsing — DTD processing prohibited, XmlResolver disabled, 50 MB character limit (XXE/billion-laughs protection)
- Path traversal protection — all cache/download paths sanitized and validated against designated roots
- URL scheme validation — only HTTP/HTTPS accepted for downloads
- File search toggle — set
HLX_DISABLE_FILE_SEARCH=trueto disable content search tools - Credential storage — tokens managed by OS keychain via
git credential, never stored in plaintext - Cache sensitivity — cached CI logs may contain secrets; treat the cache directory as sensitive. Tokens are never cached — only an irreversible 8-char hash is used for directory isolation
Known Issues
- File listing uses
ListFilesendpoint — avoids a known bug in theDetailsendpoint where file URIs are broken for subdirectories and unicode filenames (dotnet/dnceng#6072).
Requirements
- .NET 10 SDK
License
MIT