Skip to content

CLI reference

Section 5 · From docs/MCP.md on GitHub

All seven commands live in the click group cloudg.mcp.cli.mcp_group. Under cloudg mcp ... the root cloudg command prints its banner to stderr and loads -c/--config before the subcommand runs; under cloudg-mcp and python -m cloudg.mcp there is no banner and no root config. Every command except serve --transport stdio writes only its result to stdout, so output can be piped.

5.1 Options shared by serve, tools, resources, prompts, call and read

Option Default Env var Effect
-c, --config PATH the parent cloudg -c config, else CloudGConfig() cloudg config.yaml for providers, credentials and the report directory
--policy NAME_OR_FILE standard CLOUDG_MCP_POLICY Built-in profile (open, standard, strict, read_only, airgapped, audit, soc-analyst), a .yaml/.yml/.json file, or inline JSON starting with {
--dataset [NAME=]PATH none Preload a dataset; repeatable. The name defaults to the parent directory for inventory-map.json, else the file stem. With several, the last one loaded is the active dataset
--prefix TEXT "" Prepended to every tool and prompt name. At most 64 characters from A-Za-z0-9_.-; anything else stops with Error: Invalid prefix ...
--include-category CAT all Only expose these categories; repeatable; applies to tools, resources, templates and prompts
--exclude-category CAT none Hide categories; repeatable
--include-tool NAME all Only expose these tools; repeatable; resources and prompts are unaffected
--exclude-tool NAME none Hide tools; repeatable
--read-only off Drop tools that have the write_fs, cloud_access or exec capability or are annotated destructive. Loading datasets and other in-memory changes stay available, except unload_dataset, which is annotated destructive. Under standard that leaves 65 tools, one fewer than the read_only profile's 66
--audit-log FILE off CLOUDG_MCP_AUDIT_LOG Append a JSONL audit trail (section 14.2), created with mode 0600
--timeout SECONDS 300 Default per-call tool timeout. Tools with their own timeout_seconds (the live tools use 3600 and 7200) keep theirs. 0 disables the default
--registry MODULE:ATTR built-in catalog Serve a custom Registry, or a zero-argument callable returning one. MODULE must be a dotted Python module name and ATTR an attribute name; ATTR defaults to default_registry. The module is imported and the callable is run, so the value is operator code and must never come from an untrusted source. Anything that does not resolve to a Registry stops the server

--dataset paths are trusted: they are loaded even when they lie outside the workspace's allowed roots, because the operator typed them. The roots only restrict what tools may open.

The metrics middleware is always enabled from the CLI, so cloudg://metrics is always listed.

5.2 Principal options (tools, resources, prompts, call, read)

Option Effect
--role ROLE Act as a principal with this role; repeatable
--principal-id ID Principal id to act as

With neither, the CLI acts as Principal.local(): id local, roles {"default", "local"}. With either, it uses Principal(id=ID or "cli", roles=ROLES or {"default"}). Use these to see what a given role is allowed to list or call before you hand out tokens:

cloudg mcp tools --policy my-policy.yaml --role analyst
cloudg mcp call reveal_token --policy standard --role admin --args '{"token": "..."}'

5.3 serve

cloudg mcp serve [OPTIONS]
Option Default Env var Effect
--transport stdio stdio, http, streamable-http (same as http) or sse (streamable HTTP plus the deprecated /sse and /messages/ endpoints)
--host 127.0.0.1 HTTP bind address
--port 8765 HTTP port
--path /mcp Streamable HTTP endpoint
--flavor auto auto, native, sdk or fastmcp (section 7)
--auth-token TOKEN[:ROLES[:ID]] none CLOUDG_MCP_AUTH_TOKENS (whitespace-separated specs) Require Authorization: Bearer TOKEN on HTTP. ROLES is a comma list; the principal also gets default. TOKEN may be env:VARNAME. A token that contains : needs the full TOKEN:ROLES:ID form (6.3). An empty token, given directly or through an unset variable, stops the server before it starts. Repeatable. Ignored on stdio
--allowed-origin PATTERN loopback origins Allowed browser Origin values (fnmatch patterns, * for any), replacing the default. A same-origin request is accepted too, but only when the Host header was validated (loopback bind, or --allowed-host given). Checked on every bind and by every flavor; repeatable
--allowed-host PATTERN loopback names when bound to loopback Allowed Host values. Without it, a non-loopback bind does not check Host (Origin still is); repeatable
--cors-origin ORIGIN none Grant CORS to this browser origin; repeatable. Native flavor only: with sdk or fastmcp the server refuses to start
--json-response off Answer POSTs with JSON, never SSE. The fastmcp flavor accepts it for http only and refuses it with --transport sse
--stateless off Session-less streamable HTTP (handshake-era clients). Same fastmcp restriction as --json-response
--cache-ttl SECONDS off Cache results of read-only, idempotent tools for N seconds
--max-concurrency N off Cap concurrent tool calls
--page-size N 100 Items per page for list results (native flavor only)
--log-level WARNING DEBUG, INFO, WARNING or ERROR (case-insensitive). Sets the cloudg logger's level; logs go to stderr

Plus every option in 5.1. Token specs and option combinations are validated before the server starts:

$ cloudg mcp serve --auth-token env:NOPE_VAR:x
Error: Invalid value for --auth-token: Environment variable 'NOPE_VAR' for an auth token is empty
$ cloudg mcp serve --transport http --flavor sdk --cors-origin http://x
Error: --cors-origin is only supported by the native flavor (use --flavor native)
$ cloudg mcp serve --transport sse --flavor fastmcp --json-response
Error: --json-response / --stateless apply to Streamable HTTP; the fastmcp flavor cannot combine them with --transport sse
$ cloudg mcp serve --transport http --auth-token ''
Error: Invalid value for --auth-token: an empty token was given (is the variable it comes from unset?)
$ cloudg mcp serve --registry os.path
Error: Invalid value for --registry: cannot import 'os.path': module 'posixpath' has no attribute 'default_registry'

When the server stops, it calls policy.save_vault(), which writes the pseudonym vault to the policy's vault.path when one is configured (and does nothing otherwise).

Examples:

# stdio for a desktop client, strict policy, one dataset
cloudg mcp serve --policy strict --dataset prod=/srv/cloudg/reports/inventory-map.json

# streamable HTTP on loopback with two tokens and an audit log
cloudg mcp serve --transport http --auth-token env:CLOUDG_ANALYST_TOKEN:analyst:alice \
  --auth-token env:CLOUDG_ADMIN_TOKEN:admin,analyst:ops --audit-log /var/log/cloudg/mcp.jsonl

# reachable from other hosts: always with tokens and explicit Host / Origin lists
cloudg mcp serve --transport http --host 0.0.0.0 --auth-token env:CLOUDG_MCP_TOKEN:analyst \
  --allowed-host 'mcp.internal.example:*' --allowed-origin 'https://console.internal.example'

When an HTTP server starts, one line goes to stderr, for example cloudg MCP server (native, http) listening on http://127.0.0.1:8799/mcp.

5.4 tools, resources, prompts

cloudg mcp tools [--json] [--category CAT ...] [principal options] [layer options]
cloudg mcp resources [--json] [principal options] [layer options]
cloudg mcp prompts [--json] [principal options] [layer options]

Without --json they print a table; with it they print the exact wire definitions the policy shows to that principal (tools/list entries; for resources an object with resources and resourceTemplates). tools --category filters on _meta["cloudg/category"].

$ cloudg mcp prompts
                                     cloudg MCP prompts (11)
 Prompt                       Category  Arguments              Description
 security_posture_review      prompts   dataset                Whole-estate security review: risks, ...
 executive_summary            prompts   dataset                Non-technical one-page summary ...
 attack_surface_report        prompts   dataset                External attack surface: exposed ...
 investigate_asset            prompts   ref*, dataset          Deep dive on one asset: config, ...
 blast_radius_assessment      prompts   ref*, dataset          Impact of compromise or loss of one asset.
 change_impact_analysis       prompts   ref*, change, dataset  What a planned change to one asset ...
 cross_account_trust_review   prompts   dataset                IAM trust and grants across account ...
 compliance_gap_analysis      prompts   framework*, dataset    Failing controls, responsible assets ...
 remediation_plan             prompts   severity, dataset      Phased fix plan for findings ...
 incident_triage              prompts   finding_id*, dataset   Exploitability, impact and containment ...
 drift_review                 prompts   base*, target          Explain the differences between two datasets ...

(Box-drawing characters removed and descriptions trimmed.) A * marks a required argument.

How many tools each built-in profile lists for the local user, and how many keep their outputSchema (see 17.5 for why the number drops to zero):

Profile Tools listed With outputSchema Hidden compared to open
open 74 18 none
standard 73 0 reveal_token
airgapped 69 0 reveal_token, the four collecting and scanning tools
read_only 66 0 reveal_token, the four collecting and scanning tools, export_ontology, export_report, export_terraform
audit 66 0 same as read_only
strict 61 0 as read_only, plus get_asset_metadata, privacy_audit_log, sparql_query, subgraph_export, preview_transform
soc-analyst 62 0 as strict, but preview_transform stays

Of the five tools strict hides on top of read_only, the first four are RESTRICTED, so its confidential ceiling removes them. preview_transform is not RESTRICTED: strict.yaml lists it in deny_tools. soc-analyst has the same ceiling but no such deny, so it keeps that tool. Within soc-analyst, --role analyst lists 60 tools (it also loses rate_limit_status and terraform_preview), --role lead 70 (only the four collecting and scanning tools are hidden) and --role collector 69 (the five RESTRICTED tools, reveal_token among them, are hidden).

rate_limit_status is in the live category but reads only local state, so every profile keeps it.

5.5 call

cloudg mcp call TOOL [--args JSON|@FILE|-] [--raw] [principal options] [layer options]

Calls the tool in-process through the full layer (policy, transforms, middleware) and prints the structured result, or the text blocks when there is no structured result. --raw prints the whole CallToolResult wire object. --args takes a JSON object literal, @path/to.json, or - to read stdin.

Exit status is 0 on success and 1 when the tool returns isError: true, so call works in shell scripts. An unknown or hidden tool prints Error: Unknown tool: NAME and exits 1.

$ cloudg mcp call get_asset --dataset prod=inventory/inventory-map.json --args '{"ref": "bastoin"}' --raw
{
  "content": [
    {
      "type": "text",
      "text": "No asset matches the reference in this dataset. 2 close matches, see suggestions in the error data. Use find_assets(query=...) to search by name, ARN or tag."
    }
  ],
  "isError": true,
  "_meta": {
    "cloudg/error_code": -32602,
    "cloudg/error_data": {
      "value": "bastoin",
      "dataset": "prod",
      "suggestions": [
        {"id": "bastion", "name": "bastion", "arn": "arn:aws:ec2:us-east-1:111111111111:instance/i-0bast",
         "type": "EC2", "account_id": "111111111111", "dataset": "prod"},
        {"id": "admin-role", "name": "bastion-admin", "arn": "arn:aws:iam::111111111111:role/bastion-admin",
         "type": "IAM_ROLE", "account_id": "111111111111", "dataset": "prod"}
      ]
    }
  }
}
$ echo $?
1

5.6 read

cloudg mcp read URI [principal options] [layer options]

Reads a resource in-process and prints each text content as is, or the wire JSON of blob contents. Unknown URIs print Error: Unknown resource: URI and exit 1.

cloudg mcp read cloudg://datasets/prod/summary --dataset prod=inventory/inventory-map.json
cloudg mcp read 'cloudg://assets/arn:aws:ec2:us-east-1:111111111111:instance/i-0bast' --dataset ...

5.7 config

cloudg mcp config [--client claude-desktop|claude-code|cursor|vscode] [--name cloudg]
                  [--transport stdio|http] [--url http://127.0.0.1:8765/mcp]
                  [--command auto|"CMD ARGS"] [--policy P] [--dataset [NAME=]PATH ...]
                  [--read-only] [--token-env VAR]
Option Default Effect
--client claude-desktop Output format
--name cloudg Server key in the config
--transport stdio stdio launches the server; http connects to --url
--url http://127.0.0.1:8765/mcp Server URL for http
--command auto Launch command; auto tries cloudg-mcp serve, cloudg mcp serve, python -m cloudg.mcp serve. Anything else is split with shlex
--policy, --dataset, --read-only none Appended to the launch arguments (stdio only). Dataset paths are made absolute
--token-env VAR none For http: add Authorization: Bearer ${VAR} in the client's variable syntax

The Python function behind it is cloudg.mcp.cli.client_config(client, *, name, transport, url, command, serve_args, token_env) -> dict if you generate configs from code.

5.8 Environment variables

Variable Read by Meaning
CLOUDG_MCP_POLICY Policy.load(None), the --policy option Policy used when none is given: profile name, file path or inline JSON
CLOUDG_MCP_ALLOWED_ROOTS Workspace() os.pathsep-separated directories that tools may read and write (default: the current directory and the configured report directory, leaving out / and the home directory)
CLOUDG_MCP_VAULT_KEY the pseudonym vault HMAC key for pseudonyms. Without it a random key is generated per process, so pseudonyms change on every restart. Details in MCP_PRIVACY.md
CLOUDG_MCP_AUDIT_LOG --audit-log JSONL audit file
CLOUDG_MCP_AUDIT_SALT AuditLogMiddleware HMAC key for argument hashes in the audit log. Without it the key is random per process, and hashes cannot be compared across restarts
CLOUDG_MCP_AUTH_TOKENS serve --auth-token Whitespace-separated token specs
CLOUDG_DOCS_DIR the cloudg://docs/{topic} resources Directory holding DOCUMENTATION.md and INVENTORY_REFERENCE.md when the package is not run from a source checkout
AWS_WEB_IDENTITY_TOKEN_FILE the live tools' AWS preflight Checked when the config has a role ARN but no token file

A policy can also name its own vault key variable (vault.key_env); CLOUDG_MCP_VAULT_KEY is the default.