CLI reference¶
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¶
| 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¶
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¶
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.