feat(mcp): server management commands, self/atmos-pro presets, and config UX fixes @osterman (#2720)
## what- Extracts the standalone
atmos mcp installcommand (client-config installer for AI coding assistants) from the stalledosterman/pro-install-cmdbranch, leaving all Atmos Pro-specific wiring behind. - Adds full MCP server-management commands:
atmos mcp add,atmos mcp remove, andatmos mcp uninstall, rounding out the existinglist/install/export/status/testset.add/removeonly editmcp.serversinatmos.yaml(the source of truth);install/uninstallpush/pull that declared config to/from AI client config files (Claude Code, Cursor, VS Code, Codex, Gemini).- Adds built-in
selfandatmos-propresets soatmos mcp add(no arguments) works out of the box, resolving to Atmos's own MCP server or the Atmos Pro MCP server respectively. - Adding the
selfpreset interactively offers to flipmcp.enabled: trueinatmos.yamlif it isn't already, since that preset requires it to function. atmos mcp list/statusnow nudge users to runatmos mcp add atmos-prowhen Atmos Pro is configured but its MCP server hasn't been added yet.atmos mcp installwith nothing configured now interactively offers to add+install theselfpreset instead of just printing a static hint.
- Adds a new top-level
/mcpdocs landing page (website/docs/mcp/mcp.mdx) covering both directions — Atmos as an MCP client and as an MCP server — cross-linked from/aivia tabs, matching the existing/proand/ailanding pages. - Expands
atmos mcp install/uninstall's supported-client list from 5 to 15: adds Claude Desktop, Windsurf, Cline, Cline CLI, Zed, OpenCode, Goose, GitHub Copilot CLI, Antigravity, and MCPorter, alongside the existing Claude Code, Cursor, VS Code, Codex, and Gemini. Includes a new YAML config writer (for Goose) and bespoke per-client entry renderers where a client's schema differs from the commonmcpServersshape (Zed'scontext_servers, OpenCode'smcp). - Adds a
## Troubleshootingsection to theatmos mcp installdocs covering post-install behavior that varies by client (some need a restart, others need a manual settings toggle, some auto-reload). - Fixes #2628:
atmos ai skill installpreviously always wrote to~/.atmos/skills/, so VS Code / GitHub Copilot's.github/skills/couldn't discover installed skills.- Adds
--path/ATMOS_AI_SKILL_PATHto override the install directory. - Adds zero-flag multi-client skill distribution:
atmos ai skill install <name>now auto-detects which AI clients (Claude Code, VS Code/Copilot, Gemini) are in use in the current project and copies the skill into each one's well-known skill directory, mirroringatmos mcp install's detect/--client/--all-clients/interactive-picker UX.atmos ai skill uninstallgained the same--client/--all-clientssymmetry to clean up those copies. atmos ai skill install/uninstallwith no<source>/<name>now acts on every skill (every bundled skill for install, every installed skill for uninstall) instead of requiring one invocation per skill — mirrorsatmos mcp install/uninstall's existing "no server names given = act on everything configured" convention rather than adding a separate--allflag.
- Adds
- Adds
atmos config set/delete/formatUX polish:- Infers the value's type (
bool,int,float,string,yaml) from the Atmos config schema via reflection onyaml/mapstructurestruct tags, soatmos config set mcp.enabled truewrites a real boolean without needing--type=bool. - Echoes the value that was written/deleted/formatted, and shows paths relative to the current directory instead of absolute.
- Infers the value's type (
- Fixes a CLI error-message bug: a leaf command (no subcommands) whose
Argsvalidator rejects the wrong number of arguments (e.g.atmos config unset mcp.enabled trueagainstExactArgs(1)) previously reported the misleadingUnknown command mcp.enabled— implying an unrecognized subcommand — when the real problem was too many arguments. Now surfaces Cobra's own accurate validator message. - Fixes toolchain installs on macOS: verifier binaries downloaded during
atmos toolchain installcan carry thecom.apple.quarantine/com.apple.provenancexattrs, causing Gatekeeper to block execution even though the binary was already checksum/signature-verified. Strips the xattrs and ad-hoc re-signs the binary on darwin, with averifier_trust: disabledopt-out. - Adds a "Native AWS SigV4 Signing" design section to
docs/prd/atmos-mcp-integrations.md(proposal only, not implemented in this PR) for natively signing requests to AWS-hosted MCP servers using Atmos Auth identities, without a third-partymcp-proxy-for-awsbridge.
why
atmos mcp install's client-installer code was implemented on a branch entangled with unrelated, stalled Atmos Pro onboarding work; splitting it out lets the MCP work ship independently.- Early testing surfaced a real gap: there was no way to get Atmos's own MCP server, or the Atmos Pro MCP server, into an AI client's config without hand-editing
atmos.yamland running client-specific commands manually —atmos mcp add self/atmos mcp add atmos-procollapse that to one command. atmos config set mcp.enabled truesilently writing the string"true"instead of a boolean is a footgun users would hit immediately while following the new MCP add/enable flow; schema-based type inference removes the need to know or pass--type=bool.- The "Unknown command" error was actively misleading during testing of this same feature (
atmos config unset mcp.enabled true), and the root cause turned out to be a small, generally-applicable flaw in how Cobra Args-validator failures were reported for leaf commands. - The toolchain trust fix was discovered by a concurrent session working in this same worktree; bundling it into this PR (with its own commit and blog post) avoids a second churn cycle through CI/review for an unrelated one-line-symptom, multi-file fix.
- The VS Code/Copilot skill-discovery gap and the narrow 5-client MCP list were both instances of the same underlying problem: Atmos only supported a handful of AI clients by name instead of the broad set real users are on. Expanding both to the same ~15-client list (sourced from add-mcp.com/docs/agents, which also supplied the model for the new Troubleshooting guidance) closes that gap once instead of client-by-client over time, and mirroring MCP's already-proven detect/
--client/--all-clients/picker UX for skills keeps the two subsystems consistent for users learning either one.
references
- Supersedes the MCP-install portion of the stalled
osterman/pro-install-cmdbranch (Atmos Pro-specific--mcpinstall flag intentionally left behind there). - Closes #2628
feat: tags and labels standard for auth, components, and bulk selection @osterman (#2738)
## what- Adds
tagsto auth identities/providers, filterable via--tagsonatmos auth list/login/logout(composes with--providers/--identities). - Adds
metadata.tags(list, any-match) andmetadata.labels(map, all-match) to components, filterable viaatmos list components --tags/--labels. - Adds four new no-arg YAML functions —
!tags,!labels,!labels.keys,!labels.values— that return the current component's ownmetadata.tags/metadata.labels, typed, for bridging intovars(e.g.vars.tags: !labelsforterraform-null-label-style modules). - Adds
--tags/--labelsbulk-selection filtering toterraform,kubernetes,helm, andcontainercommands, composing with (not replacing)--all/--affected, via a new sharedpkg/tagspackage. - Updates the
examples/demo-authandexamples/quick-start-advancedexamples and their casts to demonstrate the new tags/labels filtering.
why
- Implements the
docs/prd/tags-and-labels-standard.mddesign (previously merged docs-only) so users can categorize and bulk-select components and identities without maintaining static name lists per stack/region/environment. - Tags (any-match) and labels (all-match, Kubernetes-selector semantics) give two matching semantics suited to different selection needs — "any of these categories" vs. "all of these key=value constraints" — consistent with existing conventions like
atmos vendor pull --tags.
references
- Implements
docs/prd/tags-and-labels-standard.md. - Relates to #1323 (
mcalhoun's open PR addingmetadata.labels+ a--selectorflag tolist/describecommands) — this PR is broader in scope (adds tags, auth tags, YAML functions, and--tags/--labelsonterraform/kubernetes/helm/container) but shares themetadata.labelsfoundation.
feat(claude): add changelog and fix-log skills, convert roadmap agent @osterman (#2751)
## what- Adds a
changelogskill consolidating blog-post authoring rules (template, tags/authors, problem-first framing, no backtick-opening prose, optional cast embeds, no Go-internals leakage) that were previously duplicated acrossCLAUDE.md, thepull-requestskill, and thedocsskill. - Adds a
fix-logskill (with avalidate-fix-doc.shscript) for durable fix records underdocs/fixes/, using a merged format that keeps the old convention's title/date line but adopts a consistent five-section structure (Summary/Context/Changes/Validation/Follow-ups). - Converts
.claude/agents/roadmap.mdinto.claude/skills/roadmap/SKILL.md, invoked viaSkillinstead ofAgent. - Trims
CLAUDE.md,pull-request/SKILL.md, anddocs/SKILL.mdto short pointers at the newchangelog/roadmapskills instead of embedding or duplicating the rules.
why
- Blog-post guidance had drifted into three places (
CLAUDE.md,pull-request,docs), withdocsindependently restating rules and solely owning one rule (no backtick-opening prose) found nowhere else. - The
roadmapagent's work is a narrow, deterministic data-editing procedure against a fixed schema — it doesn't need an isolated subagent context, matching this repo's agent-vs-skill philosophy. - Analyzing real
website/blog/*.mdxposts turned up recurring style violations (feature-first framing, backtick-opening prose, Go-internals leakage) worth codifying with concrete before/after examples.
references
- None
fix(ci): keep bot dependency PRs labeled and rebased automatically @osterman (#2752)
## what- Add a default
no-releaselabel torenovate.json, matching the label Dependabot PRs already get fromdependabot.yml. - Add
"rebaseWhen": "behind-base-branch"torenovate.jsonso Renovate keeps its own PRs current withmain. - Add a Mergify rule in
.github/mergify.ymlthat runsupdateon any open Dependabot/Renovate PR againstmainthat's conflict-free but behind — Dependabot has no native "keep up to date" option, so this fills that gap.
why
Triaging the ~18 open Dependabot/Renovate PRs turned up two systemic problems instead of one-off bad PRs:
- Every open Renovate PR (10 of them) was failing the required
PR Semver Labelscheck because it carried zero semver label. The check-cleanup logic in.github/workflows/codeql.yml'spr-semver-labelsjob only special-casesdependabot[bot], anddependabot.ymlis the only place a default label gets set — Renovate PRs never got one. main's branch protection requires the PR branch to be up to date (strict: true), but neither bot proactively rebases a clean, non-conflicting PR just becausemainmoved, so PRs accumulate asBEHINDuntil someone manually updates them.
This is purely CI/automation config — no user-facing behavior change, hence no-release.
references
feat(workflows): add native archive step type for zip/tar packaging @osterman (#2730)
## what- Add a native
type: archivestep for workflows and custom commands that packs a directory or file into azip,tar, ortgzarchive using the Go standard library only (archive/zip,archive/tar,compress/gzip) — no externalzip/tarbinary required. - Support
action: replace(always rebuild fresh) on every format, andaction: update(incremental add/refresh) forzipand uncompressedtar;action: create/action: extractandtar.bz2/tar.xzwriters are reserved in the schema for a later phase and return a typed "not yet implemented" error. - Support
subpathnesting,include/excludeglob filtering (reusing the existingpkg/utils.PathMatchmatcher), and format inference from the destination/source extension. - Reuse the existing
kind: stephook bridge instead of adding a dedicated hook kind, sokind: step+type: archiveworks as a component lifecycle hook with zero hook-side code. - Add the
docs/prd/archive-step.mdPRD, step-type reference docs, a changelog blog post, and a roadmap milestone.
why
- Packaging build artifacts (most commonly a Lambda function's
handler.zipbeforeterraform plan/apply) had no native Atmos primitive — the only option was shelling out tozip/tarvia akind: commandhook ortype: shellstep, which breaks on Windows CI and gives no typed validation. - This surfaced while migrating a Terragrunt example that used a
before_hookwrapping thezipbinary; adata "archive_file"Terraform data source was tried first but doesn't reliably run before packaging is needed. - The step ships as a step type only (not a new hook kind) to follow the precedent set by
emulator/http/container, which reach hooks purely through thekind: stepbridge — this avoids duplicating schema and hook-engine code for a capability the step registry already provides.
references
- PRD:
docs/prd/archive-step.md
🚀 Enhancements
fix(hooks): user-defined hooks now fire under bulk dispatch @osterman (#2736)
## what- Component
hooks:(the user-definedhooks.RunAllengine) now fire foratmos terraform apply/plan/deployunder--all,--affected,--components, and--query— previously they silently never ran in these bulk modes, only for single-component invocations. - Adds a
schema.ComponentNodeHooksinterface (Before/After) wired into the scheduler'sTerraformDispatcher.Dispatch— the one choke point shared by every bulk dispatch path — via a newpkg/hooks.RunPerComponentHookshelper. - A before-hook failure now aborts that component's execution before Terraform ever runs; an after-hook failure fails that component even if Terraform itself succeeded. Both respect each hook's existing
on_failure: fail|warn|ignoresetting rather than introducing new failure semantics. - Helmfile gets the same
ComponentNodeHookswiring, which is actually its first user-defined hook support of any kind (it previously had none, single- or multi-component). - A second commit removes ~2,260 net lines of confirmed-dead legacy sequential executor code and its now-orphaned tests (
terraform_executor.go,terraform_affected_graph.go, the associated_test.gofiles, and orphaned helpers interraform_utils.go/terraform_affected.go) that predated the scheduler-based dispatch path and had no live (non-test) callers left.
why
- A
before.terraform.applyhook (e.g. zipping a Lambda handler before apply) worked correctly underatmos terraform apply <component> -s <stack>but silently never ran underatmos terraform deploy --all -s <stack>for the identical component/stack/hook config — the subsequent apply then failed because the hook's output never existed. This is exactly how CI normally invokes Atmos, so any hook-dependent component broke the moment it moved from manual single-component testing to a real pipeline run. - Root cause: bulk dispatch only ever wired a CI-reporting callback (
hooks.RunCIHooks), never the user-defined hook engine (hooks.RunAll), and had no "before" seam of any kind — the code itself documented this as a known, unfixed limitation. - The dead-code removal is split into its own commit so the behavior change stays independently bisectable from the pure deletion; both were confirmed dead via call-graph analysis before removal (only test-file callers remained).
references
- N/A
fix(version-tracker): default track update/lock/status/diff to a table @osterman (#2698)
## whatatmos version track update,lock,status, anddiffnow default to a human-readable, TTY-aware table (matchingatmos version track list), instead of raw YAML.--format=yaml/--format=jsonremain available as explicit opt-ins and continue to return the original full-fidelity struct (e.g. digests, ecosystem/datasource detail) unchanged.--format=csv/--format=tsvare now also supported for these four verbs, reusing the samepkg/list/rendererpipeline already used byversion track listandsecret list.lock's default table is scoped to the resolved track's entries (not the entire multi-track lock file);--format=yaml/jsonstill dump the fullversions.lock.yamlcontents.- Updated
website/docs/cli/commands/version/track/{update,status,lock,diff}.mdxandwebsite/blog/2026-07-04-atmos-version-tracker.mdx(the source of a screenshot that surfaced this bug) to show the corrected default output. - Added test coverage in
cmd/version/track/track_test.gofor the new default-format dispatch and for full-fidelity yaml/json output.
why
atmos version track update(and its siblings) were printing raw YAML by default, which violates this project's UX convention that CLI output should be human-readable by default, with structured formats as an explicit opt-in.atmos version track listalready got this right;update/lock/status/diffshared awriteFormatted()helper that had no table option at all.