Skip to main content

meridian-governance-cli

Scriptable command-line client for Meridian Governance Router, over meridian-client. Installs as the meridian command — one short command, same shape as mta or claude.

The PyPI package is meridian-governance-cli; the command it installs is meridian. Not to be confused with the unrelated meridian-cli package also on PyPI.

Closer in spirit to the aws or stripe CLIs than to an app's own CLI: subcommands over an API, scriptable, mostly no interactive loop required — with one exception. chat absorbed Local-AI's reference implementation upstream (meridian-agent-v1.0.py, decided 2026-10-18) so customers get one governed resolve -> dispatch -> audit loop from Meridian itself, interactively or scripted, rather than writing that sequence themselves against the SDK.

Install

curl -fsSL https://dev-meridian.mytrustworthy.ai/install.sh | bash

Detects Python 3.9+, installs via pipx if present, otherwise into an isolated venv under ~/.local/share/meridian with a shim on ~/.local/bin. One command, nothing else to figure out. It also drops a small manifest at ~/.meridian/install-manifest.json recording which path it took, so uninstalling doesn't require remembering:

curl -fsSL https://dev-meridian.mytrustworthy.ai/uninstall.sh | bash

Reads that manifest to remove the right thing (pipx uninstall or the venv it created), offers to revoke your API key on the server first (meridian logout --revoke), and separately offers to delete ~/.meridian (saved credentials, chat session logs) — three confirmations, nothing deleted without asking.

Or plain pip, if you'd rather manage the venv yourself (no manifest, so uninstall.sh won't know about it — just pip uninstall when you're done):

pip install meridian-governance-cli

Get credentials

meridian signup          # new org — prompts for org name, email, password
# or
meridian login           # existing account — authenticates, mints a new key
meridian whoami           # confirm what's saved

Credentials live in ~/.meridian/config.json (mode 600 — api_key is in there in plaintext, same tradeoff mta-cli makes for its own token). Override the location with MERIDIAN_CLI_HOME if you want more than one profile.

Commands

meridian resolve my_agent [--project-id ID] [--json]
meridian explain ROUTING_LOG_ID
meridian audit ROUTING_LOG_ID --input-tokens N --output-tokens N

meridian projects create --name NAME [--quality Q] [--cost C] [--risk R]
meridian projects list

meridian agents create PROJECT_ID AGENT_NAME [--pinned-model M] [--quality-override Q] [--allowed-hubs h1,h2]
meridian agents list PROJECT_ID

meridian hubs list
meridian hubs discover

meridian keys list [--json]
meridian keys create [--name NAME]
meridian keys revoke KEY_ID

meridian catalog
meridian logout [--revoke]

meridian chat [--agent NAME] [--code-agent NAME] [--project-id ID] [--max-tokens N]
meridian chat --one-shot "message" [--json] [--max-tokens N]
meridian chat --daemon [--host HOST] [--port PORT] [--refresh-interval SECS]

Every data-returning command without a dedicated human-readable format (explain, audit, catalog, projects list, agents list) prints raw JSON — pipe into jq.

chat

Per-turn flow: resolve(agent) -> catalog/hubs join -> dispatch to the chosen hub's OpenAI-compatible endpoint -> audit. Routing, scoring and governance all live in Meridian; chat just drives the loop. OpenAI-compat hubs only for now (vLLM, Ollama, OpenAI-shaped endpoints) — bedrock/ anthropic/etc. need a protocol translator, not yet added.

  • No args: interactive REPL. /help inside it for slash commands (/catalog, /hubs, /refresh, /history, /export, /window, …).
  • Three REPL modes, switched with /chat, /code, /manage (default chat):
    • chat — plain conversation, resolve(--agent) (default agent name concierge — every org gets a default project + a concierge agent provisioned automatically on signup, so this works immediately with no setup).
    • code — same loop, different binding: resolve(--code-agent) (default agent name code) plus a one-time system preamble. Its own history, separate from chat mode's. This is not a tool-using coding agent — Meridian's dispatch layer never sends tools to the hub, so this is "a different agent/model to resolve against," nothing more. A real tool-execution loop would be new dispatch-layer work, not a REPL mode.
    • manage — no LLM calls at all: /projects, /agents, /keys (list/create, /keys revoke ID) over the account you're logged into. Same functions meridian projects/meridian agents/meridian keys call outside the REPL (meridian_cli/manage.py) — one implementation, not two that can drift.
    • /project [ID] works in any mode: with no argument, shows the active project and lists your org's projects; with an ID, switches to it (validated against your org's actual projects first). Affects the next resolve() call in chat/code mode immediately.
  • --one-shot "message": single turn, prints the response, exits. --json emits structured output (response, model, hub, routing_log_id, latency_ms, usage) instead of plain text. Exit code: 0 an answer was produced (including a token-budget-exhausted message — that's a completed, audited call, just an unhelpful one), 1 a real failure (resolve() failed, or dispatch returned an error — unreachable hub, bad model id, HTTP error), 2 misuse (--daemon and --one-shot together — mutually exclusive, now rejected instead of silently running the daemon and dropping --one-shot).
  • --daemon: long-lived local OpenAI-compatible endpoint (POST /v1/chat/completions, GET /v1/models, GET /health) — point an existing OpenAI-SDK-based app at http://127.0.0.1:8700 and it works unchanged, with Meridian's governance and audit trail in the middle. The F9 "broker" shape from Local-AI's feedback. Stateless across requests, same as the real OpenAI API: your app holds history and sends the full messages array each call.
  • --max-tokens: reasoning models can need a much larger budget than the default (8192) just to reach visible content — confirmed live against a real deployment where the same five-word prompt burned anywhere from ~2400 to the full 8192-token budget purely on reasoning, before any answer. If a response comes back as a token-budget notice instead of text, raise this — unless the prompt matches the pattern below, where raising it won't help.
  • Exact-count / constraint-satisfaction prompts can make reasoning never converge, at any budget. Isolated live against qwen3.6:35b: "Say hello in exactly five words." burned the entire reasoning budget at 8192, then again at 16384 — raising --max-tokens didn't make it converge faster, it just filled whatever ceiling it was given. The same model answers the unconstrained version ("Say hello.") normally in 186 reasoning tokens, and a substantive non-counting prompt normally in ~700. So this isn't the model's reasoning budget being too small — it's a specific model failure mode where exact-count constraints seem to trap it in verifying the count. Not a Meridian or CLI bug; a model characteristic to know about before raising --max-tokens in response to a stuck "exactly N words/items" prompt — raising it will not fix that case.

hubs discover

Probes localhost for a local Ollama (:11434/api/tags) or vLLM (:8000/v1/models), proposes registering what it finds (models included), and asks for confirmation before actually registering anything — it never writes to your org's catalog without that confirmation. If nothing responds on localhost, it falls back to prompting for a provider + URL manually, probes that instead, and asks for confirmation the same way. Same wizard from meridian hubs discover and /discover inside meridian chat's manage mode (meridian_cli/manage.py + chat.py's cmd_manage_discover) — one implementation either way.

Deliberately only probes localhost, never a LAN subnet: unlike a single-household tool that can hardcode its own known boxes' IPs, Meridian is multi-tenant — there's no fixed list of "this customer's boxes" to guess at. localhost is the one address that means the same thing for every caller; anything else goes through the manual-entry fallback instead of a scan.

Also why this is a client-side probe at all, not a call to the backend's own /discover endpoint: on a SaaS instance, "localhost" from the server's perspective means the server's own container, not your laptop. Discovery and model-listing both happen in the CLI process so they can actually reach localhost on the machine meridian is running on; the resulting model list is registered directly via register_models, never by asking the server to re-discover something it structurally cannot reach.

What this deliberately doesn't do

  • hubs discover only knows Ollama and vLLM, and only probes localhost. No cloud-hub registration commands (bedrock/anthropic/openrouter/etc.) — those need credentials entered deliberately, not something to probe for. Console-only for now.

Metadata

Release files for meridian-governance-cli 0.8.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 meridian-governance-cli 0.8.0
File Size Uploaded Provenance
meridian_governance_cli-0.8.0.tar.gz 34.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for meridian-governance-cli 0.8.0
File Interpreter ABI Platform
meridian_governance_cli-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 64.0 kB

Release files / meridian_governance_cli-0.8.0.tar.gz

Download URL meridian_governance_cli-0.8.0.tar.gz
Size 34.6 kB
Tags Source
SHA-256 checksum
How to use checksums
75d433147e2c57881818d8301c63f0d961eb3510977f9bf0f8d2d45f69793cec
BLAKE2b-256 checksum
How to use checksums
84671c59216659864ae9b4760175c76bd1f6133e0ee2e39fcd1a9a16e9bb9afe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / meridian_governance_cli-0.8.0-py3-none-any.whl

Download URL meridian_governance_cli-0.8.0-py3-none-any.whl
Size 29.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f6955d5e44b85a5f855a832df97020025f3c6ef213a0d7d5b380c3b629f5ff0f
BLAKE2b-256 checksum
How to use checksums
cd3be7dc5357e17de1d271b61ec93718869bff83179a663e78881419e2a30fe1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for meridian-governance-cli 0.8.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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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