metergraph-cli 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,7 +21,8 @@ Published on npm as `latest`. See the [changelog](https://github.com/metergraph/
21
21
  ## Quickstart
22
22
 
23
23
  **1. Set up the project.** Run this in your project directory. It opens your browser to
24
- sign in and choose a workspace, asks a workspace member to approve an ingest-only key,
24
+ sign in and choose a workspace, asks a workspace member to approve an ingest-only key
25
+ (on the same page as sign in, where the service supports it),
25
26
  writes `METERGRAPH_APP_TOKEN` and `METERGRAPH_INGEST_URL` to a private `.env`, and
26
27
  installs the skill for your coding agent.
27
28
 
@@ -47,11 +48,12 @@ Then read what Metergraph has recorded:
47
48
 
48
49
  ```sh
49
50
  npx --yes metergraph-cli@latest usage --days 7 # calls, errors, cost and tokens per route per day
51
+ npx --yes metergraph-cli@latest usage --group-by route --today # most expensive route today (UTC)
50
52
  npx --yes metergraph-cli@latest traces --limit 20 # one page of trace metadata
51
53
  ```
52
54
 
53
55
  To install it globally instead, run `npm install -g metergraph-cli@latest`. The command is
54
- `metergraph`. Pin an exact version, such as `metergraph-cli@0.2.1`, when a
56
+ `metergraph`. Pin an exact version, such as `metergraph-cli@0.2.2`, when a
55
57
  script must not change with the `latest` tag. Previews use the `next` tag.
56
58
 
57
59
  ## Requirements
@@ -67,8 +69,11 @@ Sign in needs a service that offers Metadata-only CLI grants and grant revocatio
67
69
  service without them is reported as unsupported, and the CLI never falls back to
68
70
  broader access. `setup` also needs the deployment's ingest bootstrap API and approval by
69
71
  a workspace member. Sign in needs a browser on the same machine. From SSH sessions,
70
- cloud development environments and CI, `login` and `setup` hand off to a local machine
71
- instead (exit 6).
72
+ cloud development environments and CI, `login` and `setup` exit 6 with a structured
73
+ `next_action`: a person creates a coding-agent key with Metadata access on the Keys
74
+ page and saves it as the secret `METERGRAPH_AGENT_TOKEN`, and the read commands use it
75
+ after verifying the origin, workspace, scope and capabilities. Ingest setup still runs
76
+ on a local machine.
72
77
 
73
78
  ## Using it with a coding agent
74
79
 
@@ -132,8 +137,8 @@ Suggested `AGENTS.md` or `CLAUDE.md` lines for a project that uses Metergraph:
132
137
  | --- | --- | --- |
133
138
  | `setup` | Sign in, choose a workspace, write the ingest key to `.env`, record the repository, install the skill | Yes |
134
139
  | `verify` | Confirm that one exact trace, by trace ID or request ID, reached the workspace | Yes |
135
- | `usage` | Daily calls, errors, cost, tokens and latency per route | Yes |
136
- | `traces` | One page of trace metadata, with a cursor for the next page | Yes |
140
+ | `usage` | Daily calls, errors, cost, tokens and latency per route, or cost grouped by provider, model, route or UTC day with exact filters, rankings and a prior-period comparison; filtered by exact metadata or grouped by one metadata key | Yes |
141
+ | `traces` | One page of trace metadata, filtered by route, status or exact metadata, with a cursor for the next page | Yes |
137
142
  | `routes` | Routes with call counts and evaluation contract status | Yes |
138
143
  | `status` | Whether the project is signed in, and to which workspace | Yes |
139
144
  | `context` | Workspace details and retention | Yes |
@@ -170,7 +175,7 @@ Exit codes are stable. Changing one is a breaking change. Codes 10 to 18 are not
170
175
  | 13 | `revocation_unconfirmed` | Local credentials were removed, but the service did not confirm that the grant was revoked. |
171
176
  | 14 | `capability_unavailable` | The service does not make this read available to the project's Metadata grant. No data was read. |
172
177
  | 15 | `permission_denied` | The service refused this read for the signed in grant, for example for a missing scope or permission. |
173
- | 16 | `rate_limited` | The service asked the CLI to slow down. Nothing was retried. Try again later. |
178
+ | 16 | `rate_limited` | The service asked the CLI to slow down. Try again later. Only setup retries, after a short Retry-After, to keep its approval. |
174
179
  | 17 | `cancelled` | A read command was interrupted before it finished. Read commands never change workspace configuration or telemetry. |
175
180
  | 18 | `action_required` | A person must act before the command can finish; `data.next_action` says what to do. `login` and `setup` with `--json --no-browser` exit with this as soon as an approval URL exists (`next_action.kind: "open_url"`), while a background process waits for the approval until `--timeout-ms`. After approval, run the same command again to continue. |
176
181
 
@@ -57,6 +57,53 @@ pass the same `--workspace` UUID each time, and give each repository its own
57
57
  identity. The SDK warning that repository identity is not configured does not
58
58
  stop capture.
59
59
 
60
+ ## Pick the CLI path for where the agent runs
61
+
62
+ Check `metergraph --help` first. If the read commands' help lists
63
+ `--agent-token-file`, the CLI also accepts a coding-agent key with Metadata
64
+ access from the `METERGRAPH_AGENT_TOKEN` environment variable. Use one sequence:
65
+
66
+ | Where the agent runs | Sequence |
67
+ |---|---|
68
+ | Local shell, hosted, new workspace | `metergraph setup --runtime local --client <client> --json --no-browser`, rerun after each approval, then `metergraph status --json`. Add `--signup` for a person with no hosted account. |
69
+ | Local shell, hosted, existing workspace | `metergraph login --runtime local --workspace <uuid> --json --no-browser`, then `metergraph status --json`. |
70
+ | Local shell, customer-local or customer-owned AWS | `metergraph setup --runtime local --client <client> --deployment customer-local` (or `byoc`) `--url <origin> --workspace <uuid> --json --no-browser`. Follow its `next_action` for each prerequisite, and add `--confirm-prerequisites` only after the person confirms them. Then `metergraph status --json`. Without `--deployment`, setup and login go to the hosted service. |
71
+ | Local container (Docker, local dev container) | As a local shell. If the browser cannot reach the container's sign in callback, use the key sequence below. |
72
+ | SSH, Codespaces, Gitpod, Cloud Shell, remote dev container, CI: hosted, customer-local or customer-owned AWS | The person signs in (or signs up, on the hosted service only) on their own device, opens the Keys page of the deployment and workspace the agent should read, creates a coding-agent key with Metadata access, and saves it in this environment's secret settings, or as a CI secret, named `METERGRAPH_AGENT_TOKEN`. Then run `metergraph status --json` and `metergraph usage --json`. Add `--workspace <uuid>` to make a key for another workspace fail. For a non-hosted deployment, pass `--url <origin>` (or set `METERGRAPH_URL`) with its address. |
73
+ | Any runtime: open-source self-hosted server | The CLI read commands do not support the open-source server; use its MCP route (below). There are no accounts and no Keys page: the operator gives the person one of the server's `MG_AGENT_TOKENS` values for the client's private MCP settings. |
74
+ | Has a Metadata key but no saved sign in | The same key sequence. If the project has a `.metergraph/project.json`, it pins the origin and workspace the key must match. |
75
+
76
+ `.metergraph/project.json` names the deployment origin and workspace ID. In a
77
+ public repository, keep it untracked (for example in `.gitignore`) and do not
78
+ commit it: that would publish the workspace ID. In CI for a public repository,
79
+ the read commands' JSON includes workspace IDs and per-route usage: write it to a file or
80
+ a masked step instead of the public job log, and print only what the person
81
+ needs.
82
+
83
+ Before answering a cost question from the CLI, confirm the workspace and what
84
+ it offers: `metergraph status --json` (the verified workspace must be the one
85
+ the person means) and `metergraph capabilities --json`. Then run the smallest
86
+ bounded `usage` read that answers it. Grouped usage needs a CLI that has it:
87
+ run `metergraph help usage` first, and use `--group-by` only when that help
88
+ lists it (for example `metergraph usage --group-by route --today --json` for
89
+ the most expensive route today). An older CLI has only
90
+ `metergraph usage --days N --json` (cost per route and day); say that grouping
91
+ by provider, model or metadata needs a newer CLI or the
92
+ `metergraph_get_usage_aggregates` tool, and do not invent flags. Grouped
93
+ usage windows are UTC days; plain `usage --days N` is the last N x 24 hours,
94
+ rolling. `metergraph-investigate` covers the rest. A deployment without grouped
95
+ usage exits 14 with a `next_action` that names the read it supports.
96
+
97
+ When `login`, `setup` or a read cannot finish where it runs, it prints
98
+ `data.next_action`. Follow it: `use_agent_key` lists the person's steps and the
99
+ commands to run afterwards; `replace_agent_key`, `use_metadata_only_key` and
100
+ `use_workspace_key` mean the key was refused, was broader than Metadata, or
101
+ belongs to another workspace. Never ask the person to paste the key into chat or
102
+ a command, and never put it in a project file. Ingest setup cannot run over SSH,
103
+ in a cloud workspace or in CI; it runs on the person's machine. The Metadata key
104
+ is separate from the application's ingest key and never replaces it. Debug and
105
+ Replay keys are refused by the CLI, not reduced.
106
+
60
107
  ## Route honestly
61
108
 
62
109
  | Client and runtime | Hosted | Customer-local bundle | Customer AWS | Open source self-hosted |
@@ -103,10 +150,12 @@ rather than improvising the analysis steps.
103
150
 
104
151
  ## When already connected: bounded investigation
105
152
 
106
- 1. Call `metergraph_get_workspace_context` and `metergraph_get_capabilities` first. Verify the intended workspace, deployment profile, available tools, required scopes and privacy classes. Stop on wrong workspace, unavailable capabilities or failed authorization.
107
- 2. Confirm the question, route and explicit time window. Choose the smallest available tool: bounded usage/route metadata for aggregate questions, incidents for known anomalies, or `metergraph_query_traces` with a small limit (start with 1) for a failed/anomalous trace. Follow cursors only within the agreed bound. Never request generic SQL or unbounded data.
153
+ Read through the `metergraph` CLI first when it is installed and can read the workspace: it returns the same Metadata evidence as the MCP tools in far fewer tokens. Use the MCP tools for what the CLI does not cover (incidents, ingestion health, reports, retained content and replay) and in clients with no shell. Being connected means either one; stop for setup only when neither can read the workspace.
154
+
155
+ 1. Confirm the workspace and what it offers. With the CLI: `metergraph status --json` (continue when `data.authenticated` is true and `data.workspace.match` is true. When `match` is null because no workspace was pinned, name `data.workspace.actual` and continue only after the person confirms it is the one they mean. Otherwise follow `data.next_action`) and `metergraph capabilities --json`. With MCP: `metergraph_get_workspace_context`, then `metergraph_get_capabilities`. Verify the intended workspace, deployment profile, available reads, required scopes and privacy classes. A wrong workspace or failed authorization on one path is a reason to use the other path when it can read the intended workspace; stop when neither can, or when the capability the question needs is unavailable.
156
+ 2. Confirm the question, route and explicit UTC time window. Choose the smallest available read, and always pass `--json` to the CLI: grouped usage (`metergraph usage --group-by ... --json`, or `metergraph_get_usage_aggregates`) or `metergraph usage --days N --json` / route metadata for aggregate questions, `metergraph traces --days N --limit 1 --json` with `--route` when known and `--status error` only for a failed trace (or `metergraph_query_traces` with a small limit, which also takes an exact trace or request ID) for a failed or anomalous trace, and the incidents tool for known anomalies. Check that `metergraph help <command>` lists an option before using it, and use the matching MCP tool when it does not. Follow cursors only within the agreed bound. Never request generic SQL or unbounded data.
108
157
  3. Start with metadata. Retained-content debugging and report evidence are separate content-bearing tools; confirm the user's content intent and capability before calling them. Replay is a separate scope and can send data to a provider and spend money. Obtain explicit bounded execution/data-egress authority before any replay. Read access grants no eval-write, analysis-run or production-mutation authority.
109
- 4. Explain the failure or anomaly using only returned evidence. Cite workspace, time window, provenance, trace/report IDs, completeness/coverage, warnings and working app links when returned. Do not fabricate links. Distinguish no data, incomplete evidence, unavailable capability, classification pending, stale/mismatched identity, failed analysis and missing report. An empty result alone does not prove absence of traffic.
158
+ 4. Explain the failure or anomaly using only returned evidence. Cite workspace, time window, provenance, trace/report IDs, completeness/coverage, warnings and working app links when returned. Do not fabricate links. Distinguish no data, incomplete evidence, unavailable capability, classification pending, stale/mismatched identity, failed analysis and missing report. An empty result alone does not prove absence of traffic. Never conclude that a commit, deploy or config change did or did not change cost from source code or a git diff alone; a diff is a hypothesis until bounded usage reads for the same route before and after the change reached production agree, with completeness and warnings cited.
110
159
  5. An optional approved replay must be eligible, bounded and non-persistent. Do not claim that it changed production telemetry or fixed the application. Refuse autonomous dashboard mutations or production configuration changes.
111
160
 
112
161
  For an empty workspace, use https://www.metergraph.dev/docs/agents/instrument-a-repo/. If `metergraph setup` already ran in this project, it delivered the ingest credential: the project's `.env` holds `METERGRAPH_APP_TOKEN` and `METERGRAPH_INGEST_URL`. Use them; do not ask for or create another key, and never print their values. The SDK reads the process environment, not `.env`, so load it when you run the application (for example `set -a; . ./.env; set +a`, python-dotenv or `node --env-file=.env`). Otherwise confirm the ingestion destination and obtain a separate ingest credential securely. Ask before running anything that calls a paid model provider, and do not initiate provider calls or paid work merely to validate setup. Confirm the exact trace with `metergraph verify`; if the CLI is not installed, or `metergraph --version` lacks `verify`, use `npx --yes metergraph-cli@latest verify`. Ingestion accepted, processed, retained content, classification readiness and analysis readiness are distinct states. Label synthetic/demo fixtures; they do not prove a first real application trace.
@@ -3,7 +3,7 @@
3
3
  "name": "metergraph",
4
4
  "file": "SKILL.md",
5
5
  "source_url": "https://www.metergraph.dev/SKILL.md",
6
- "sha256": "d527a67efc42a55245b9c2c8fd2687c1f31f553e154a6242d0f31466993e314e",
7
- "size": 13666,
8
- "revision": "sha256-d527a67efc42"
6
+ "sha256": "ebf784673eff0280d85974504f93c683a1dabfb54156c9d27e091131462e9d5f",
7
+ "size": 19764,
8
+ "revision": "sha256-ebf784673eff"
9
9
  }
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "manifest_version": 1,
3
3
  "repository": "metergraph/skills",
4
- "commit": "839da0fc82c331598db78022610620390a81703a",
4
+ "commit": "39411ce1eceed6929ab3e5c4c20649f02e8eabb4",
5
5
  "skills": [
6
6
  {
7
7
  "name": "metergraph-analyze",
8
- "sha256": "e8bf1debf62aa7d33d3dd363e5d95d8f781e8ca23cde4619b5bb4133dd47a7a5",
9
- "size": 9360,
10
- "revision": "sha256-e8bf1debf62a"
8
+ "sha256": "c45756ad1d33a360c7c845ca299d8c1ab1e2b8162865f635bc25c78358c2a1e6",
9
+ "size": 12148,
10
+ "revision": "sha256-c45756ad1d33"
11
11
  },
12
12
  {
13
13
  "name": "metergraph-candidates",
@@ -23,9 +23,9 @@
23
23
  },
24
24
  {
25
25
  "name": "metergraph-investigate",
26
- "sha256": "775dbe7ca1100b17638d0905302ef1324694a60b90372c6c3df7b598a8f70438",
27
- "size": 6091,
28
- "revision": "sha256-775dbe7ca110"
26
+ "sha256": "470579faf9e8b5f1a562060d51ec5a71d1b9c47213403a2447dd40af56a74869",
27
+ "size": 19331,
28
+ "revision": "sha256-470579faf9e8"
29
29
  },
30
30
  {
31
31
  "name": "metergraph-iterate",
@@ -47,15 +47,15 @@
47
47
  },
48
48
  {
49
49
  "name": "metergraph-onboarding",
50
- "sha256": "6e6f418933f37b34437035ab3171344d43f37dee3770815b19f97efb62e5f1d1",
51
- "size": 10267,
52
- "revision": "sha256-6e6f418933f3"
50
+ "sha256": "409d105f3b042514fd347a1fffc5489a94c01d1bc8aac55800e8c9f13a66a2c1",
51
+ "size": 10751,
52
+ "revision": "sha256-409d105f3b04"
53
53
  },
54
54
  {
55
55
  "name": "metergraph-report",
56
- "sha256": "58add8f6a32191e6fc01ca533a700e190a8e6da412e619bca38c65a481ea9fb5",
57
- "size": 4521,
58
- "revision": "sha256-58add8f6a321"
56
+ "sha256": "0cdbeb684c4f7f87932784db832f376cff984fe0220a1167f1a73edd550450b7",
57
+ "size": 5471,
58
+ "revision": "sha256-0cdbeb684c4f"
59
59
  },
60
60
  {
61
61
  "name": "metergraph-workloads",
@@ -116,14 +116,72 @@ restate what you are starting and proceed. Otherwise ask and wait.
116
116
  the returned run instead of starting another. On `precondition_failed`,
117
117
  report the `reason` and stop.
118
118
  4. Poll `metergraph_get_analysis_run` with the `run_id` at a modest interval
119
- (about every 30 to 60 seconds) and tell the user the status as it changes.
120
- Do not restart a run because it is slow. Stop polling when the status is
121
- final, and say if it failed, with its `error_code`.
119
+ (about every 30 to 60 seconds). Do not restart a run because it is slow.
120
+ Stop polling when the status is final, and say if it failed, with its
121
+ `error_code`.
122
+ 5. After the start and after every poll, write a progress report in the
123
+ shape shown under "Show run progress" before you wait, set a timer or
124
+ schedule the next check. The start response already carries
125
+ `run.stages`, so the reply that starts the run has one too. A one-line
126
+ status such as "The run is exporting; I'll check again in a minute" is
127
+ not a progress report. The graphic is required every time.
128
+
129
+ ## Show run progress
130
+
131
+ Draw the stages from `run.stages.items`, in the order returned. Use each
132
+ item's `state` exactly as given. Never decide a stage's state from `status`,
133
+ trace names or the report.
134
+
135
+ Render one line with a marker per state: `✓` completed, `●` current, `✗`
136
+ failed, `–` skipped, `○` not started. For example:
137
+
138
+ ```
139
+ ✓ queued → ✓ exporting → ✓ analyzing → ● classified → ○ importing → ○ completed
140
+ ```
141
+
142
+ If the client can draw a richer inline graphic, you may use one. Keep the
143
+ same order and states, and make the states distinct without relying on
144
+ colour alone.
145
+
146
+ Under the graphic, always write a plain-text line that says the same thing:
147
+ the stages that are done, the current stage, and the stages still to come.
148
+
149
+ Every check-in, including the reply that starts the run, has this shape:
150
+
151
+ ````
152
+ Run 3f2a… at 19:34 UTC:
153
+
154
+ ```
155
+ ✓ queued → ✓ exporting → ✓ analyzing → ● classified → ○ importing → ○ completed
156
+ ```
157
+ Done: queued, exporting, analyzing. Now: classified, since 19:23 UTC
158
+ (11 minutes, unchanged since the last check). Still to come: importing,
159
+ completed. No error, no report yet. Next check in about a minute.
160
+ ````
161
+
162
+ - **Time in the current stage.** Take it from the current item's
163
+ `entered_at` and the response's `provenance.generated_at`, which is the
164
+ check time. Say when you last checked, and say plainly when the stage has
165
+ not changed since the last check. Never give a percentage, an ETA or a
166
+ time remaining. The run data supports neither.
167
+ - **Finished.** On `completed`, draw the items as returned, like any other
168
+ check: the stages the run passed through are `✓`, and a stage it never
169
+ entered stays `–` skipped. When `report.available` is true, read the
170
+ report (step 6) and give any link it returns exactly as given. When it is
171
+ false, say plainly that no report exists yet.
172
+ - **Failed.** On `failed`, mark the stage it stopped in with `✗`, give the
173
+ `error_code`, and leave the later stages `○`.
174
+ - **No stage data.** When `run.stages.available` is false, or `stages` is
175
+ missing, show no graphic. Say the stage breakdown is unavailable for this
176
+ run (`unavailable_reason`, when present). Report the `status`, the
177
+ timestamps, the `error_code` if the run failed, and whether a report
178
+ exists. Never mark stages complete by inference.
122
179
 
123
180
  ## 6. Read the report and recommend
124
181
 
125
182
  When `report.available` is true, call `metergraph_get_report` with
126
- `report.analysis_id`. Use the summary format and report link in
183
+ `report.analysis_id`. While it is false, do not look for a report under
184
+ another or guessed ID. Use the summary format and report link in
127
185
  `metergraph-report` when it is installed. Present:
128
186
 
129
187
  - the report `outcome`: `opportunities`, `no_opportunities`,
@@ -1,50 +1,183 @@
1
1
  ---
2
2
  name: metergraph-investigate
3
- description: Investigate a failed or anomalous LLM trace, usage spike or incident through an already connected Metergraph Agent Access server, answering only from bounded, cited evidence. Use after setup is verified; use the metergraph skill first when not connected.
3
+ description: Investigate a failed or anomalous LLM trace, usage spike, cost question (which route, provider or model costs most, a daily trend, a change from the prior period), incident or whether a code change moved cost, reading through the metergraph CLI first (fewer tokens) or a connected Metergraph Agent Access MCP server, answering only from bounded, cited evidence. Use after setup is verified; use the metergraph skill first when neither the CLI nor MCP can read the workspace.
4
4
  ---
5
5
 
6
6
  # Investigate with Metergraph evidence
7
7
 
8
8
  This skill expands the bounded investigation section of the public `metergraph`
9
- skill. It adds no tools, scopes or authority. When no Metergraph MCP server is
10
- connected, or the user has not chosen a client, runtime and deployment, stop and
11
- use the `metergraph` skill for setup routing. Never ask for, echo or store a
9
+ skill. It adds no tools, scopes or authority. Never ask for, echo or store a
12
10
  key.
13
11
 
12
+ Read through the `metergraph` CLI first when it is installed and can read the
13
+ workspace: it returns the same Metadata evidence as the MCP tools in far fewer
14
+ tokens. Use the MCP tools for what the CLI does not cover (incidents, ingestion
15
+ health, reports, retained content and replay) and in clients that have no
16
+ shell. Stop and use the `metergraph` skill for setup routing only when neither
17
+ the CLI nor a Metergraph MCP server can read the workspace, or the user has not
18
+ chosen a client, runtime and deployment.
19
+
14
20
  ## 1. Discover before reading data
15
21
 
22
+ **With the CLI** (preferred when `metergraph --help` lists `status`,
23
+ `capabilities` and `usage`; an older CLI can run but lack them):
24
+
25
+ 1. Run `metergraph status --json`. Continue when it exits 0 with
26
+ `data.authenticated: true` and `data.workspace.match: true`. When `match`
27
+ is null because no workspace was pinned (a coding-agent key without
28
+ `--workspace`), name `data.workspace.actual` and continue only after the
29
+ person confirms it is the workspace they mean. When it prints
30
+ `data.next_action` (for example sign in again, or the coding-agent Metadata
31
+ key path for a remote session), follow it; never ask for the key in chat.
32
+ 2. Run `metergraph capabilities --json` and build the plan from
33
+ `data.result.agent` (each capability's `available`, `privacy_class` and
34
+ `required_scope`) and `data.result.bounds` (`max_days`, `max_rows`).
35
+ 3. Before using any option, check that `metergraph help <command>` lists it.
36
+ An option an older CLI lacks is a reason to use the matching MCP tool, not
37
+ to guess a flag. Always pass `--json`: the evidence is under `data.result`,
38
+ with `data.notices` and `data.next_action` beside it, and the exit code is
39
+ the outcome (for example 14 when the deployment does not offer the read).
40
+
41
+ **With MCP** (no CLI, no shell, or a read the CLI does not offer):
42
+
16
43
  1. Call `metergraph_get_workspace_context`, then `metergraph_get_capabilities`.
17
44
  Do not call any other Metergraph tool first.
18
- 2. Confirm with the user that `provenance.workspace_id`, the workspace name and
19
- `provenance.deployment_profile` match the installation they intend. A staging
20
- or demo workspace is not their production workspace. Stop on a mismatch,
21
- an authorization error, or revoked or expired access, and tell the human to
22
- reconnect through their client's secure flow.
23
- 3. Build the tool plan only from what the server returns: the workspace
45
+ 2. Build the tool plan only from what the server returns: the workspace
24
46
  context's `access.scopes`, and each capability under the capabilities
25
47
  response's `agent` object with its `available`, `privacy_class` (metadata,
26
48
  content or replay), `required_scope`, `mutates` and `external_calls`, plus
27
49
  `bounds` (`max_days`, `max_rows`). Treat the tools your client lists as the
28
- only callable tools. A tool or capability that is absent, or has
29
- `available: false`, is a blocker to report, not something to
30
- guess, retry under another name or replace with a dashboard API, generic SQL
31
- or a shell command. Some deployments are content-blind or have no replay,
32
- incidents, ingestion health or reports.
50
+ only callable tools.
51
+
52
+ Either way, confirm with the user that the workspace ID, the workspace name
53
+ and the deployment profile match the installation they intend. A staging or
54
+ demo workspace is not their production workspace. A mismatch, an
55
+ authorization error, or revoked or expired access belongs to that one path:
56
+ when the CLI cannot read the intended workspace but a connected MCP server
57
+ can (or the other way round), use the path that can. Stop and tell the human
58
+ to reconnect through their client's secure flow only when neither the CLI nor
59
+ MCP can verify the intended workspace. A capability that is absent, or
60
+ has `available: false`, is a blocker to report, not something to guess, retry
61
+ under another name or replace with a dashboard API, generic SQL or an ad hoc
62
+ shell command. Some deployments are content-blind or have no replay,
63
+ incidents, ingestion health or reports.
33
64
 
34
65
  ## 2. Fix the question and the bound
35
66
 
36
- Restate the question, the route or workload if known, and an explicit window in
37
- whole days (tools accept `days` from 1 to 90; default to the shortest window
38
- that answers the question). Then choose the smallest tool that can answer:
67
+ Restate the question, the route or workload if known, and an explicit UTC
68
+ window. For grouped usage, windows are UTC: `days: N` means the current UTC
69
+ day so far plus the `N - 1` whole UTC days before it, so today is always
70
+ partial; `since` (inclusive) and `until` (exclusive) give an exact window.
71
+ Plain `metergraph usage --days N` (no grouping) is instead the last N x 24
72
+ hours, rolling and not aligned to UTC days; say so when you report it. Keep
73
+ every window within 90 days and within the capabilities' `bounds.max_days`.
74
+ Default to the shortest window that answers the question. Then choose the
75
+ smallest tool that can answer:
76
+
77
+ | Question | CLI (check its help first) | MCP tool | Bound |
78
+ | --- | --- | --- | --- |
79
+ | Which route, provider or model costs most; spend with one provider or model; a daily trend | `metergraph usage --group-by ... --json` with `--route`, `--provider`, `--model` or `--environment` | `metergraph_get_usage_aggregates` with `group_by` and the same exact filters | `--days`/`days` or `--since`/`--until`, small limit |
80
+ | Daily cost per route, when grouped usage is unavailable | `metergraph usage --days N --json` | `metergraph_get_usage` | days, small limit |
81
+ | Which routes exist or are replay eligible | `metergraph routes --json` | `metergraph_list_routes` | none needed |
82
+ | Is data arriving | none; use MCP | `metergraph_get_ingestion_health` | `days` |
83
+ | Known alert or anomaly | none; use MCP | `metergraph_list_incidents` | small `limit` |
84
+ | Cost or calls for one prompt, workflow or other tag value | `metergraph usage --group-by metadata.KEY --json`, or `--metadata KEY=VALUE` | `metergraph_get_usage_aggregates` with `group_by` including `metadata.<key>`, or `metadata` (exact pairs) | days, small limit |
85
+ | One failed call | `metergraph traces --status error --limit 1 --json` (also `--route`, `--metadata`) | `metergraph_query_traces` with `status` set to the failure status, and `route`, `trace_id`, `request_id` or `metadata` | limit 1 to 5 |
86
+ | One costly call (it may have succeeded) | `metergraph traces --route NAME --limit 5 --json` with no `--status`, then compare cost on that page | `metergraph_query_traces` with `route`, `trace_id`, `request_id` or `metadata`, no `status` | limit 1 to 5 |
87
+ | Which routes are slow (route level, not one call) | `metergraph usage --group-by route --sort p95-latency --json`; CLI `traces` returns no latency | `metergraph_get_usage_aggregates` grouped by route | days, small limit |
88
+ | One slow call | none; CLI `traces` has no latency, so ask for its trace or request ID and use MCP, or send the person to the trace in the app | `metergraph_query_traces` with that `trace_id` or `request_id` | limit 1 |
89
+ | A finished analysis | none; use MCP | `metergraph_list_reports`, then `metergraph_get_report` | small `limit` |
39
90
 
40
- | Question | First tool | Bound |
91
+ Use grouped usage (`metergraph usage --group-by`, or
92
+ `metergraph_get_usage_aggregates`) only when capabilities list
93
+ `usage_aggregates` as available. On an older deployment it is absent: say so,
94
+ answer what `metergraph_get_usage` can (cost per route and day), and do not
95
+ rebuild provider or model totals by paging traces.
96
+
97
+ ### Cost questions
98
+
99
+ `metergraph_get_usage_aggregates` groups calls by any ordered subset of
100
+ `provider`, `model`, `route` and `day` (the call's UTC calendar day), plus at
101
+ most one `metadata.<key>`. Cost is
102
+ attributed per call, so a trace that used two providers adds each call's cost
103
+ to that call's provider. Groups come back highest cost first, at most `limit`
104
+ of them; `totals` cover every matching call whatever the limit. From a shell,
105
+ the `metergraph` CLI sends the same read and prints the same evidence. Check
106
+ `metergraph help usage` first: use these commands only when it lists
107
+ `--group-by`. An older CLI has only `metergraph usage --days N` (cost per route
108
+ and day); use the tool instead, or say a newer CLI is needed.
109
+
110
+ | Question | Tool arguments | CLI |
41
111
  | --- | --- | --- |
42
- | How much, how many, which route costs most | `metergraph_get_usage` | `days`, small `limit` |
43
- | Which routes exist or are replay eligible | `metergraph_list_routes` | none needed |
44
- | Is data arriving | `metergraph_get_ingestion_health` | `days` |
45
- | Known alert or anomaly | `metergraph_list_incidents` | small `limit` |
46
- | One failed, slow or costly call | `metergraph_query_traces` with `status`, `route`, `trace_id` or `request_id` | `limit` 1 to 5 |
47
- | A finished analysis | `metergraph_list_reports`, then `metergraph_get_report` | small `limit` |
112
+ | Most expensive route today | `{"group_by": ["route"], "days": 1}` | `metergraph usage --group-by route --today --json` |
113
+ | Spend with one provider | `{"group_by": ["provider"], "provider": ["<name>"], "days": 7}` | `metergraph usage --group-by provider --provider <name> --days 7 --json` |
114
+ | Spend by model | `{"group_by": ["model"], "days": 7}` | `metergraph usage --group-by model --days 7 --json` |
115
+ | One route's daily trend | `{"group_by": ["day"], "route": ["<route>"], "days": 14}` | `metergraph usage --route <route> --group-by day --days 14 --json` |
116
+ | Cost by prompt ID | `{"group_by": ["metadata.prompt_id"], "days": 7}` | `metergraph usage --group-by metadata.prompt_id --days 7 --json` |
117
+ | Today against yesterday | two reads: `days: 1`, then `since`/`until` for the prior UTC day | `metergraph usage --group-by route --today --compare previous --json` |
118
+
119
+ Rank only what was returned. When `coverage.rows.truncated` is true (more
120
+ groups matched than `limit`), a ranking by calls, errors or latency covers
121
+ only the listed groups. Before naming a "top" group, raise `limit` to
122
+ `coverage.rows.matched` when that is at most 200 and at most the
123
+ capabilities' `bounds.max_rows`; otherwise narrow the read. Name the latency
124
+ you report:
125
+ `latency.avg_ms` is the mean over `latency.samples` calls that reported one,
126
+ and `latency.p95_ms` is an estimate from a fixed histogram
127
+ (`p95_method: histogram_estimate`), not an exact percentile.
128
+
129
+ A "costs most" answer needs exact pricing. When `pricing.cost` is
130
+ `lower_bound` (the overall `pricing`, or a group's own), some calls are not
131
+ fully priced, so a group with more unpriced calls can look cheaper than it is.
132
+ Name the group with the highest recorded cost as a lower bound and say the
133
+ ranking is not definitive until pricing is exact; never call it the most
134
+ expensive outright.
135
+
136
+ A prior-period comparison needs two reads with stated windows. Comparing
137
+ today so far with all of yesterday is not like for like: say so, or compare
138
+ with yesterday up to the same UTC time (`--compare previous-same-time` in the
139
+ CLI). A group missing from a truncated window is unknown, not zero.
140
+
141
+ A route is not a prompt. One route (for example `support-reply`) can carry
142
+ several prompt IDs in its metadata (SDK tags), and one prompt ID can run on
143
+ several routes. To answer "which prompt costs most", group by the metadata key
144
+ alone, so each prompt is one row across every route. Add `route` to `group_by`
145
+ only to show how a route splits by prompt. Keep the same `days` on every
146
+ follow-up read, so the examples come from the window you just summarised:
147
+
148
+ ```json
149
+ {"name": "metergraph_get_usage_aggregates", "arguments": {"days": 7, "group_by": ["metadata.prompt_id"]}}
150
+ {"name": "metergraph_query_traces", "arguments": {"days": 7, "metadata": {"prompt_id": "reply-v3"}, "limit": 3}}
151
+ ```
152
+
153
+ Metadata values are exact, case-sensitive opaque strings: `reply-v3` does not
154
+ match `reply-v30`, a call without the key never matches a filter, and several
155
+ filter keys must all match. In a read grouped by a metadata key, calls in the
156
+ window that have no value for the grouped key form the group whose value is
157
+ `null`, and `coverage.reasons` names `metadata_key_missing` (or
158
+ `metadata_value_ineligible` for a non-string or oversize value). Report that
159
+ group rather than dropping it. If you also filtered on that same key, no such
160
+ calls remain, so there is no `null` group. A `metadata_key_mostly_missing`
161
+ warning means most calls or cost have no value for the key; say so, and
162
+ suggest checking the key's spelling. `metadata_groups.unlisted` accounts for
163
+ every call not listed with a value: `other_values` (values past the limit, with
164
+ a group count), `missing_key` and `ineligible_value`. Report the spend in
165
+ `missing_key` plus `ineligible_value` as not attributed to any value of the
166
+ key, so a prompt ranking never reads as the whole spend. When
167
+ `coverage.rows.truncated` is true, the listed groups are not the whole window:
168
+ quote `totals` for the window and `coverage.rows.matched` as the number of
169
+ matched groups. That counts groups, not distinct values of the key: with
170
+ `group_by: ["route", "metadata.prompt_id"]` one prompt used on three routes is
171
+ three groups. For a count of distinct values, read with the metadata key as
172
+ the only dimension.
173
+
174
+ The CLI can run the same reads (`metergraph usage --group-by
175
+ metadata.prompt_id --days 7 --json`, where `--group-by-metadata prompt_id` is
176
+ the same dimension and the way to give a key that contains a comma, and
177
+ `metergraph traces --days 7 --metadata prompt_id=reply-v3 --json`) only in versions that
178
+ have these options. Check that `metergraph usage --help` lists `--group-by`
179
+ (and `metergraph traces --help` lists `--metadata`) before you suggest them,
180
+ and otherwise use the tools above.
48
181
 
49
182
  Follow `next_cursor` only while you are still inside the agreed bound, and say
50
183
  how many pages you read. Never request every trace, every report or unbounded
@@ -69,14 +202,23 @@ point to the human action in the Metergraph app instead.
69
202
 
70
203
  ## 4. Answer only from returned evidence
71
204
 
72
- Every answer cites, from the tool responses you actually received:
205
+ Every answer cites, from the CLI results or tool responses you actually
206
+ received (a CLI result carries the same fields under `data.result`, plus
207
+ `data.notices`):
73
208
 
74
209
  - **Workspace**: workspace name and `provenance.workspace_id`, plus
75
210
  `deployment_profile`.
76
- - **Window**: `window.since` to `window.until` (or `days`) for each read.
211
+ - **Window**: `window.since` to `window.until` in UTC (or `days`) for each
212
+ read, and whether the last day is partial.
77
213
  - **Provenance**: the tool name, `provenance.source` and `generated_at`.
78
214
  - **Evidence and completeness**: `evidence.rows`, `evidence.complete`,
79
- `page.truncated` and whether a `next_cursor` was left unread.
215
+ `page.truncated` and whether a `next_cursor` was left unread; for usage,
216
+ `coverage.completeness` with its `reasons`, `coverage.rows.matched` against
217
+ the rows returned, and whether a total is the whole population
218
+ (`totals.scope`) or only the returned rows.
219
+ - **Pricing and attribution**: `pricing.cost` (`exact` or a `lower_bound`) and
220
+ `attribution.level`. A trace cost is a whole-trace total and is never split
221
+ by provider.
80
222
  - **Warnings**: every returned `warnings[].code` with its message. Say "no
81
223
  warnings returned" when the list is empty.
82
224
  - **Identifiers and links**: trace, request, incident or analysis IDs, and
@@ -87,16 +229,61 @@ window, incomplete or truncated evidence, capability unavailable in this
87
229
  deployment, classification pending, ambiguous request match
88
230
  (`request_match.ambiguous`), failed analysis, and missing report. An empty page
89
231
  does not prove there was no traffic; check ingestion health or a longer bounded
90
- window before saying so. Do not round, extrapolate or invent totals.
232
+ window before saying so. Do not round, extrapolate or invent totals. Never
233
+ attribute a cost change, or the absence of one, to a code change from source
234
+ code or a git diff; see section 5.
235
+
236
+ ## 5. Code changes and cost: never decide from the diff
237
+
238
+ When the question is whether a commit, pull request, deploy or config change
239
+ raised, lowered or did not change cost, a git diff is never the evidence. Read
240
+ it to form a hypothesis (a different model, a longer prompt, a new retry loop, a
241
+ changed `max_tokens`, an added tool call), then say it is a hypothesis. Do not
242
+ answer "this commit increased cost", "this commit saved money" or "this commit
243
+ has no cost impact" from code alone, including a diff that touches no model or
244
+ prompt code: wrappers, retries, caching, routing, traffic and provider prices
245
+ change spend without appearing in the diff.
246
+
247
+ A cost conclusion needs returned Metergraph usage evidence:
248
+
249
+ 1. Establish when the change reached the traffic Metergraph observes, from
250
+ recorded commit or deployment identity (a deploy record, or the commit the
251
+ telemetry itself carries). Merge time is not deploy time. Without that
252
+ record, any before-and-after reading is only a time correlation: call it
253
+ that, give the windows, and do not attribute the difference to the commit.
254
+ If no one knows when it shipped, say the comparison cannot be anchored.
255
+ 2. Read usage for the same route or workload over comparable, bounded UTC
256
+ windows before and after that point (`metergraph_get_usage_aggregates`
257
+ with explicit `since` and `until` and the same filters, or
258
+ `metergraph_get_usage` where grouped usage is unavailable), and cite both
259
+ reads as in section 4. Windows of equal length avoid comparing a partial
260
+ day with a whole one. Filter both reads to the environment the deployment
261
+ record names (`environment`), so traffic in another environment that
262
+ shares the route is not credited to the change. If that environment
263
+ cannot be isolated (for example the grant or deployment has no
264
+ environment filter), say the cost impact is unknown.
265
+ 3. Compare per-call cost and token counts as well as totals, so a traffic
266
+ change is not mistaken for a code change. Name what else changed in the
267
+ window when you know it (traffic, provider prices, other deploys).
268
+ 4. If either window is incomplete, truncated or warned, if the windows are not
269
+ comparable, or if the returned data cannot separate traffic by the change,
270
+ state that the cost impact is unknown and name the next bounded check. Do
271
+ not fill the gap from the diff.
272
+
273
+ Present the diff and the usage evidence separately: what the code suggests,
274
+ and what Metergraph measured.
91
275
 
92
- ## 5. Worked workflow: one failed trace
276
+ ## 6. Worked workflow: one failed trace
93
277
 
94
- 1. Discover: context, then capabilities. Confirm the workspace with the user.
95
- 2. Find: `metergraph_query_traces` with `status` set to the failure status, the
96
- agreed `days` and `limit` 1. If the user gave a request ID and the match is
278
+ 1. Discover: `metergraph status --json` and `metergraph capabilities --json`
279
+ (or the context and capabilities tools). Confirm the workspace with the user.
280
+ 2. Find: `metergraph traces --status error --days N --limit 1 --json` (or
281
+ `metergraph_query_traces` with `status` set to the failure status, the
282
+ agreed `days` and `limit` 1). If the user gave a request ID and the match is
97
283
  ambiguous, list the candidates and ask which one.
98
- 3. Explain from metadata: route, model, provider, tokens, cost, latency, status
99
- and span count, with the citations above. If metadata is enough, stop here.
284
+ 3. Explain from metadata: route, model, provider, tokens, cost, status and
285
+ span count, plus latency only when the read returns it (CLI `traces` does
286
+ not; never infer it from timestamps), with the citations above. If metadata is enough, stop here.
100
287
  4. Optional content: with consent, `metergraph_get_trace` for that one trace to
101
288
  read the error, tool calls and root-cause classification.
102
289
  5. Optional replay: only with explicit approval and when the trace is replay
@@ -117,6 +117,13 @@ connection, paid eval rule or provider invocation just to manufacture proof.
117
117
  still needed and the next eval selection action. A synthetic demonstration
118
118
  does not meet real application or hosted pilot acceptance.
119
119
 
120
+ A person's first cost question after setup ("which route costs most today")
121
+ is a bounded usage read, not a trace scan: `metergraph usage --group-by route
122
+ --today --json` from a CLI whose `metergraph help usage` lists `--group-by`, or
123
+ `metergraph_get_usage_aggregates` when capabilities list `usage_aggregates`. Use `metergraph-investigate` for it. A
124
+ remote or CI agent reads with the coding-agent Metadata key path in the
125
+ `metergraph` skill, never with the application's ingest key.
126
+
120
127
  Verify the named trace through the connected client's advertised MCP tools,
121
128
  with an explicit source label and the same workspace/deployment before and
122
129
  after any permitted content read. The older trace-debug contract has no