Skip to main content

Ermes CLI and optional compatibility MCP

This package gives Ermes operators a CLI-first surface for explicit-project API reads and guarded writes. It also retains a 10-tool read-only MCP for temporary compatibility, but normal agent sessions leave that server unregistered.

Version 1.0.0 (DEV-2261). Verified 2026-10-20.

Install the CLI

pipx install ermes-cli              # from PyPI; `pipx upgrade ermes-cli` later
ermes version

Or run the installer, which also checks Python/pipx and verifies the entrypoints:

./cli/scripts/install-cli.sh                  # latest release
./cli/scripts/install-cli.sh --version=1.0.0  # pinned
./cli/scripts/install-cli.sh --branch=main    # developers: straight from git

Releases are cut by pushing a tag ermes-cli-vX.Y.Z equal to cli/pyproject.toml's version (.github/workflows/publish-cli.yml runs the tests, builds the wheel, publishes to PyPI and attaches it to a GitHub Release). install-cli.sh is idempotent and safe to re-run for upgrades.

If a temporary rollback needs fixed typed schemas, register the compatibility MCP only for one explicit project:

./cli/scripts/install-mcp.sh --project=<project-name-or-id>

Remove it after the rollback window. Removal preserves Auth0 tokens, project keys, and every file under ~/.ermes:

./cli/scripts/install-mcp.sh --uninstall

Never run ermes logout as part of registration removal; logout deletes the Keychain session and projects.yml.

ermes login                             # Auth0 device flow + auto-discover projects
ermes projects list                     # show cached projects; keys stay masked
ermes use <project-name-or-id>           # interactive active-project selection
ermes whoami                            # local selection only; not API liveness
ermes projects refresh                  # re-sync after admins add you to new projects
ermes logout                            # delete Keychain tokens + projects.yml

ermes login runs an Auth0 Device Authorization Flow (RFC 8628) against the shared dev-hqiwr-re.us.auth0.com tenant (same one publinker-cli uses, so the authenticated user is your Auth0 identity). After authentication:

  1. Trades the Auth0 access token for an Ermes admin JWT via /api/cli/exchange (the account's email must be verified in Auth0).
  2. Lists every project you can access via /api/admin/projects/all.
  3. Caches the list in ~/.ermes/projects.yml (chmod 600) — no keys are minted at login.
  4. Auto-selects the first project as active.
  5. On the first command against a project, mints your own sk_ key for it via POST /api/admin/projects/:id/cli-key, named cli-<email>-<hostname> and bound to your admin account (revoked when you are removed from the project). Keys from the old cli-managed-<hostname> scheme are ignored.

Auth0 tokens live in the OS keychain (ermes-cli service). On a headless box with no keyring backend they fall back to ~/.ermes/auth0-tokens.json (chmod 600). Per-project sk_ keys live in ~/.ermes/projects.yml.

For interactive work, ermes use <project-name-or-id> persists the active project. For every script or agent command, pin scope without changing that shared default:

ERMES_PROJECT=<project-name-or-id> ermes --json campaigns list --limit 1

Resolution order: --profile, ERMES_API_KEY, ERMES_PROJECT, the active project, then the legacy ERMES_PROFILE / config.yml default. ERMES_PROFILE is deprecated and prints a warning; it can no longer override an explicit project or projects.yml.

For CI or a machine without a browser, skip login and pass a key directly:

ERMES_API_KEY=sk_... ERMES_BASE_URL=https://ermes.publinker.com ermes --json campaigns list

ermes whoami reports the local selection; ermes whoami --check additionally makes one authenticated read and exits 1 if the key is rejected.

Requires: AUTH_CLI_AUDIENCE=https://ermes.publinker.com/api set on the Ermes API (SSM env-file). Without it, /api/cli/exchange returns 404 and ermes login errors out with a clear hint.

Legacy: single-profile manual setup

ermes config init (or ermes config add) still works for the one-project, manual-paste flow. It writes ~/.ermes/config.yml with the legacy profile shape; resolve_profile() falls back to it when no projects.yml exists. The command now emits a deprecation warning pointing at ermes login.

Write-mode safety (default-blocked mutations)

Every POST / PATCH / PUT / DELETE through the CLI is blocked by default. Reads work freely; mutations require an explicit unlock. These interactive commands act on the project deliberately selected with ermes use:

ermes write status                    # show current state
ermes write allow                     # unlock for 30 minutes
ermes write allow --minutes 5         # custom window (1-1440 min)
ermes write allow --persistent        # no expiry (until `write deny`)
ermes write deny                      # close the window immediately

For one reviewed scripted mutation, keep both project and write authorization process-scoped:

ERMES_PROJECT=<owned-project-name-or-id> ERMES_ALLOW_WRITES=1 \
  ermes --json <resource-group> <mutation> <arguments>

Ermes has no general server-side dry run. Read the current resource before the mutation, keep the scope to an owned project, then read it again to verify the requested state. Never leave ERMES_ALLOW_WRITES exported in a shared shell.

State lives in ~/.ermes/state.yml (chmod 600), separate from projects.yml so refreshing projects doesn't clobber the write window. ermes whoami shows the current write status.

The optional compatibility MCP remains fixed at 10 read-only tools. Mutations stay CLI-only so every agent write must carry explicit project scope and one-process ERMES_ALLOW_WRITES=1 authorization.

Metrics

These examples assume that you deliberately selected an interactive project with ermes use. In scripts, prefix the same commands with ERMES_PROJECT=<project-name-or-id>.

ermes metrics                           # last 7 days, type=trigger, channel=email (UI default)
ermes metrics --type blast --days 30    # last 30 days, blast campaigns only
ermes metrics --type all --start 2026-05-01 --end 2026-05-31
ermes metrics --channel push --days 7   # push campaigns
ermes metrics --channel all --days 7    # both, as two labelled blocks
ermes --json metrics --type all         # raw JSON for piping

Per-campaign breakdown: sends, opens, clicks, bounces, complaints, plus computed open/click/CTOR rates. Aggregate summary row at the bottom. Server caps at 100 rows / 90-day range; the CLI surfaces both as warnings.

Channels

--channel / -c takes email (default) | push | all. The API accepts only email and push, lowercase — the CLI folds the case so --channel PUSH works instead of 400ing, and defaults to email, which is also what the server assumes when the param is omitted, so the default output is unchanged.

There is no combined mode server-side: --channel all issues one request per channel and prints two labelled blocks (--json nests them under channels). The two are never summed — push has no opens or bounces, so the rates aren't comparable.

--json is a ROOT flag, not a metrics flag — ermes --json metrics …, never ermes metrics --json (that exits with No such option: --json). This catches people out because every example that shows JSON also shows a subcommand. The per-campaign rows land under results (not campaigns or rows), alongside channel, summary, truncated and aggregated_through:

{"channel": "push", "results": [{"campaign_id": 156139, "name": "...",
 "sends": 61, "failed": 0, "clicks": 0, "click_rate": 0, "opens": 0}],
 "summary": {"campaign_count": 13, "sends": 499, "failed": 67, "clicks": 25},
 "truncated": false, "aggregated_through": "2026-08-04"}

For --channel all the shape nests instead: {"channel": "all", "channels": {"email": {…}, "push": {…}}}, so a consumer must branch on channel before reading results.

Push renders the UI's columns — Sent (= sends + failed), Delivered (= sends), Failed, Clicks, Click %. Opens / Open % / CTOR / Bounces / Spam are omitted: push has no open or bounce event, so they are always zero and only read as breakage.

aggregated_through in the response is the newest local day with data for that channel+type, capped at the day the hourly rollup has marked complete for the project. Today, and yesterday until the first run past local midnight, are returned as partial "so far" numbers, so the default window ends yesterday, like the UI; pass --end to include today. The empty-state message prints the frontier so an empty window is legible.

Tool surface

CLI (ermes ...)

Group Commands
campaigns list, get, create, update, delete, trigger (--external-id, optional --email/--phone/--device-token/--locale/--timezone)
templates list, get, create, update, delete
lists list, get, create, update, delete
journeys list, get, create, update, delete
users list, get, delete, events, lists
keys list, get, create, update, delete — uses your Auth0 admin session, never a project key
subscriptions, tags, providers, ingest, export, config …
login, logout, whoami, use, projects Auth0 device-flow auth + multi-project context
metrics Campaign metrics, email or push (mirrors UI Metrics view)
write allow / deny / status Control the write-mode guard (mutations blocked by default)

Full surface: ermes --help.

Optional compatibility MCP (ermes-mcp)

The 10-tool read-only MCP remains for temporary Claude Code rollback only. Normal sessions use the CLI so they can pin project scope per command without loading fixed schemas at startup. install-mcp.sh --project=<name-or-id> writes an explicit ERMES_PROJECT; ermes use doesn't retarget that process.

The CLI covers every compatibility read and is the preferred path:

Tool Purpose
whoami Current profile + base URL (no API call)
list_campaigns(limit?, status?) Campaigns; use CLI campaigns list --status when filtering
get_campaign(campaign_id) One campaign
list_templates(limit?) Templates
get_template(template_id) One template
list_lists(limit?) Contact lists
list_journeys(limit?) Automation journeys
list_users(limit?, email?) Users; use CLI users list --q for exact search
get_user(user_id) One user; the CLI argument is the external ID
get_user_events(user_id, limit?) Recent events; the CLI argument is the external ID

Where in the wider stack

Ermes is a CLI-first integration in the Martech-DM/martech-mcps registry. That repo's ermes/README.md and ermes/skills/ermes-ops/SKILL.md are the cross-fleet setup and operating runbooks. The compatibility MCP stays unregistered during normal operation.

CLI source history

The ermes_cli/ package was originally bundled inside the legacy ermes-skill. It now lives here as the single code source of truth. The versioned operating skill lives at martech-mcps/ermes/skills/ermes-ops and invokes this installed package rather than carrying a copy.

Metadata

Release files for ermes-cli 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ermes-cli 1.0.0
File Size Uploaded Provenance
ermes_cli-1.0.0.tar.gz 98.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for ermes-cli 1.0.0
File Interpreter ABI Platform
ermes_cli-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 168.5 kB

Release files / ermes_cli-1.0.0.tar.gz

Download URL ermes_cli-1.0.0.tar.gz
Size 98.8 kB
Tags Source
SHA-256 checksum
How to use checksums
2061ac49380d54234a3dbf236417de23aa7b4eeaf252ece7931a2cad2fe62740
BLAKE2b-256 checksum
How to use checksums
231355878fe1274f2db9d4f5abce34e84cd158f1a5544b1376ee3276d89b9151
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / ermes_cli-1.0.0-py3-none-any.whl

Download URL ermes_cli-1.0.0-py3-none-any.whl
Size 69.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
81367aa8e50557a036d3bbcec657a2dd160a47a6a006031df6b74253f64d4ef2
BLAKE2b-256 checksum
How to use checksums
71eadc7660febb738509fe6882a37c8950841c00f2d6249d2f32906f5f9d83e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Security for ermes-cli 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ermes_cli-1.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page