# Command line

The `costfluent` command runs each tool of the [MCP server](/developers/mcp) from a terminal, a script or a CI job. A command is the tool's name with hyphens and takes the tool's arguments as flags, so it reads exactly what the tool reads, with the same capability check, validation and result. It prints JSON for a script and a table for you. A read tool's command only reads; a write tool's command creates or changes what the tool does, and only once an Owner lets AI agents create and change objects ([the MCP page](/developers/mcp) says how). No command deletes anything in Costfluent or touches your cloud. `costfluent estimate`, described below, posts a Terraform pull request's cost estimate from any CI.

## Before you start

<PlanAvailability feature="ApiTokens" plan="Pro" />

- An API token. Create one as the MCP guide's [Create a token](/developers/mcp#create-a-token) describes, from the **Read only** preset; a CI job takes its own token. On your own computer you can [sign in](#sign-in) instead.
- A macOS, Linux or Windows machine, amd64 or arm64.

## Install

Download the archive for your platform from the [latest release](https://github.com/costfluent/costfluent-mcp/releases/latest) and put `costfluent` on your `PATH`. The archive holds both `costfluent` and the MCP server, `costfluent-mcp`. Or build them with Go:

```bash
go install github.com/costfluent/costfluent-mcp/cmd/costfluent@latest
go install github.com/costfluent/costfluent-mcp/cmd/costfluent-mcp@latest
```

Each release carries a build provenance attestation, which `gh attestation verify <archive> --repo costfluent/costfluent-mcp` checks.

## Run a command

<Stepper>

1. Set `COSTFLUENT_API_TOKEN` to the token, or [sign in](#sign-in). `COSTFLUENT_BASE_URL` is optional, defaults to `https://api.costfluent.com` and must use https.
2. Run `costfluent whoami` to see what the API says of the token: its name, its workspace and its capabilities.
3. Run a command, such as the query below. `costfluent <command> --help` lists a command's flags.

</Stepper>

```bash
export COSTFLUENT_API_TOKEN=<token>
costfluent whoami
costfluent query-costs --start-date 2026-10-01 --end-date 2026-10-31 --group-by ServiceName
costfluent list-budgets --output json | jq '.items[] | select(.state == "over")'
```

## Sign in

Instead of a token, `costfluent login` signs you in through your browser, where you approve read access for one organization, as [Sign in from an app](/api/authentication#sign-in-from-an-app) describes. Commands then run without `COSTFLUENT_API_TOKEN`; a token that is set still wins. `costfluent logout` revokes the sign-in and forgets it on this computer, also when the revocation cannot reach Costfluent.

```bash
costfluent login
costfluent whoami
costfluent logout
```

- The sign-in is kept in the operating system's credential store: the macOS Keychain, the Secret Service on Linux or the Windows Credential Manager. On a computer without one, `costfluent login --insecure-storage` keeps it in `costfluent/credentials.json` under `XDG_CONFIG_HOME`, or the system's configuration directory, readable by you alone, and a command refuses the file once others can read it. Without the flag, the sign-in fails there and revokes what it received.
- The page that opens shows `costfluent login` as an app Costfluent has not verified and that returns only to your computer, since its answer comes back to the command on this computer: allow it when you ran the command yourself.
- `--no-browser` prints the address to open instead of opening your browser. `--timeout` sets how long the command waits for you to approve, such as `--timeout 10m`; it waits 5 minutes otherwise.
- `COSTFLUENT_BASE_URL` chooses the API as for every command, and each API keeps its own sign-in.
- When the sign-in has ended or was revoked, a command exits with 3 and asks you to run `costfluent login` again.

## Commands

| Command | What it does | Capability |
|---|---|---|
| `costfluent list-workspaces` | The workspaces the token can read | **View workspaces** |
| `costfluent query-costs` | Cost, usage or resource count over a date range, grouped by up to three dimensions and narrowed by a cost filter | **View costs** |
| `costfluent list-cost-reports` | A workspace's saved cost reports | **View cost reports** |
| `costfluent get-cost-report-data` | The numbers a saved report shows now | **View costs**, and **View cost reports** for a report that charts a business metric |
| `costfluent list-annotations` | A workspace's annotations by date | **View cost reports** |
| `costfluent list-budgets` | Budgets with their current spend and state | **View budgets** |
| `costfluent list-anomalies` | Detected cost anomalies, newest first | **View anomalies** |
| `costfluent list-savings-items` | A workspace's savings findings with their estimated monthly saving and status | **View savings** |
| `costfluent list-commitments` | The savings plans, reservations and committed use discounts a workspace holds | **View costs** |
| `costfluent get-commitment-coverage` | How much of the eligible usage commitments covered over a date range | **View costs** |
| `costfluent get-kubernetes-efficiency` | Cost, idle cost and CPU and memory used against requested per cluster, namespace or workload | **View costs** |
| `costfluent create-annotation` | Adds a note on a date to one or more saved cost reports | **Update cost reports** |
| `costfluent create-cost-report` | Saves a new cost report | **Create cost reports** |
| `costfluent update-cost-report` | Changes a saved cost report; what the call leaves out stays as it is | **Update cost reports** |
| `costfluent create-budget` | Creates a budget | **Create budgets** |
| `costfluent update-budget` | Changes a budget; what the call leaves out, its alerts included, stays as it is | **Update budgets** |
| `costfluent create-cost-alert` | Creates a cost alert | **Manage cost alerts** |
| `costfluent update-cost-alert` | Changes a cost alert; what the call leaves out stays as it is | **Manage cost alerts** |
| `costfluent pause-cost-alert` | Pauses a cost alert | **Manage cost alerts** |
| `costfluent resume-cost-alert` | Resumes a paused cost alert | **Manage cost alerts** |
| `costfluent create-dashboard` | Creates a dashboard | **Create dashboards** |
| `costfluent add-dashboard-widget` | Adds a widget to a dashboard | **Update dashboards**, and **View cost reports** |
| `costfluent acknowledge-anomaly` | Acknowledges a detected cost anomaly | **Acknowledge anomalies** |
| `costfluent set-savings-item-status` | Sets a savings finding to **Accepted**, **Implemented** or **Declined**, a decline with its reason | **Respond to savings** |
| `costfluent assign-savings-item-owner` | Sets who owns a savings finding | **Manage savings** |
| `costfluent search-docs` | Searches the Costfluent documentation, in English or German, for the sections that answer a question; needs no token | None |
| `costfluent get-doc-page` | Prints a page of the Costfluent documentation, or one section of it, as markdown; needs no token | None |

A flag is the tool's argument with hyphens: the `start_date` of `query_costs` is `--start-date`. A flag that takes a list, such as `--group-by`, is given once for each value.

<FeatureStatus feature="CommitmentCoverage" />

`costfluent list-commitments` and `costfluent get-commitment-coverage` read the commitments a workspace's connections hold and how much of the eligible usage they covered.

## Connect an assistant

`costfluent mcp install` prints the entry that starts `costfluent-mcp` in your assistant, or adds it to the assistant's configuration file with `--write`.

<Stepper>

1. Run `costfluent mcp install --client <client>` with `claude-code`, `cursor`, `codex`, `vscode` or `copilot`, and check the entry it prints.
2. Run it again with `--write` to add the entry to the file the table below names. `--project` writes Cursor's or Codex's project file in the current directory instead of your own.
3. Restart the assistant, from a shell where `COSTFLUENT_API_TOKEN` is set.

</Stepper>

| Client | File |
|---|---|
| `claude-code` | `.mcp.json` in the current directory |
| `cursor` | `~/.cursor/mcp.json`, or `.cursor/mcp.json` with `--project` |
| `codex` | `~/.codex/config.toml`, or `.codex/config.toml` with `--project` |
| `vscode` | `.vscode/mcp.json` in the current directory |
| `copilot` | `~/.copilot/mcp-config.json`, or `mcp-config.json` in `COPILOT_HOME` |

:::note
The entry holds no token unless you ask for it. Claude Code, Cursor, Codex and Copilot CLI read it from `COSTFLUENT_API_TOKEN` in their own environment, and VS Code asks for it the first time it starts the server. With `--write --embed-token` the entry holds the token from `COSTFLUENT_API_TOKEN` instead, for an assistant started where that variable is not set, and the file becomes readable by you alone: keep it out of version control, and revoke the token when you stop using the assistant. Cursor started from the Dock does not see your shell's variables: start it from a shell, or point the entry's `envFile` at a file that sets the variable. Claude Code started without the variable passes the server the text `${COSTFLUENT_API_TOKEN}` rather than a token, which the API refuses.
:::

## Post cost estimates from CI

`costfluent estimate submit` posts the estimate Infracost makes for a Terraform pull request to Costfluent, and `costfluent estimate apply` records that the change was applied, with the ids Terraform gave the resources it created or replaced. With those ids Costfluent finds each resource in your billing data and compares the estimate with the cost that followed. The two commands do from any CI what Costfluent's cost estimate GitHub Action does in GitHub Actions, converting Infracost's and Terraform's JSON with the Action's own programs, so both post the same estimate.

<Stepper>

1. Open **Settings**, then **API tokens**, and select **Create API token**. Under **Access**, choose the workspace the estimates belong to; under **Start from**, choose **Read only**, then clear every capability and select **Submit cost estimates**, the only one the commands need.
2. Save the token as a masked secret of the pipeline and pass it to the job as `COSTFLUENT_API_TOKEN`. No flag takes the token.
3. In the job that runs on merge or pull requests, write Infracost's diff as JSON (`infracost diff --format json` with Infracost v0.10, `infracost diff --json` with Infracost v2) and the saved plan with `terraform show -json`, then run `costfluent estimate submit`.
4. In the job that applies the merged change, apply the saved plan, write the plan and then the state with `terraform show -json`, and run `costfluent estimate apply` with the merged pull request's number.

</Stepper>

| Flag | What it takes |
|---|---|
| `--workspace-id` | The workspace the estimate belongs to; required |
| `--repository` | The repository, such as `acme/infra`; required |
| `--pull-request` | The pull or merge request number; required |
| `--infracost-json` | For `submit`, Infracost's JSON; required |
| `--terraform-plan-json` | `terraform show -json` of the saved plan: optional for `submit`, where it gives each changed resource the ids Costfluent measures its cost by, and required for `apply` |
| `--terraform-state-json` | For `apply`, `terraform show -json` of the state after the apply; required |
| `--commit`, `--url`, `--infracost-version` | For `submit`, the commit, an https link to the pull request and the Infracost version, kept with the estimate |
| `--fail-above` | For `submit`, the most the planned monthly change may be, in the estimate's currency |

### GitLab CI

This job runs on merge requests, after a `plan` job that keeps `infracost.json` and `plan.json` as artifacts. `COSTFLUENT_API_TOKEN` is a masked CI/CD variable.

```yaml
cost-estimate:
  image: golang:1.27.4
  needs: [plan]
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - go install github.com/costfluent/costfluent-mcp/cmd/costfluent@latest
    - >-
      costfluent estimate submit
      --workspace-id wsp_8f3k2m
      --repository "$CI_PROJECT_PATH"
      --pull-request "$CI_MERGE_REQUEST_IID"
      --infracost-json infracost.json
      --terraform-plan-json plan.json
      --commit "$CI_COMMIT_SHA"
      --url "$CI_MERGE_REQUEST_PROJECT_URL/-/merge_requests/$CI_MERGE_REQUEST_IID"
      --fail-above 500
```

### Azure Pipelines

This pipeline runs on pull requests in Azure Repos, which a build validation policy on the target branch starts. `COSTFLUENT_API_TOKEN` is a secret pipeline variable, which a step sees only when its `env` maps it.

```yaml
trigger: none

pool:
  vmImage: ubuntu-24.04

steps:
  - task: GoTool@0
    inputs:
      version: 1.27.4
  - script: |
      go install github.com/costfluent/costfluent-mcp/cmd/costfluent@latest
      echo "##vso[task.prependpath]$HOME/go/bin"
    displayName: Install costfluent
  # Run Terraform and Infracost here, writing plan.json and infracost.json.
  - script: >-
      costfluent estimate submit
      --workspace-id wsp_8f3k2m
      --repository "$(Build.Repository.Name)"
      --pull-request "$(System.PullRequest.PullRequestId)"
      --infracost-json infracost.json
      --terraform-plan-json plan.json
      --commit "$(System.PullRequest.SourceCommitId)"
      --fail-above 500
    displayName: Post the cost estimate
    env:
      COSTFLUENT_API_TOKEN: $(COSTFLUENT_API_TOKEN)
```

### Record the apply

After the merged change is applied from its saved plan, in any CI:

```bash
terraform show -json tfplan > plan.json
terraform apply -input=false tfplan
terraform show -json > state.json
costfluent estimate apply --workspace-id wsp_8f3k2m --repository acme/infra --pull-request 42 \
  --terraform-plan-json plan.json --terraform-state-json state.json
```

- `submit` creates the pull request's estimate on its first post and replaces it on each later one until the change is applied. A diff that changes no priced resource posts nothing, and the command exits 0.
- Both commands print the estimate's receipt: its id and state and, from `submit`, the planned monthly cost before and after, the change and how many resources Infracost could not price, as Costfluent computed them in the estimate's currency. A line on stderr says whether the estimate was created or updated.
- `--fail-above` is judged after the receipt is printed and compares the exact amounts: a change equal to `--fail-above` passes, a change above it exits 4, and a negative change never does. When Costfluent answers without the planned totals, a warning says `--fail-above` is not applied and the command exits 0, rather than failing every pull request on a change it cannot read.
- `apply` warns of each resource the plan created or replaced that the state holds no id for, and leaves it out. A pull request with no estimate in the workspace has nothing to apply: the command says so with the API's reason and exits 0.
- A wrong or missing flag, or an input file that is not JSON, exits 2 before any request.

## How it behaves

- A command prints JSON when its output goes to a file, a pipe or a CI log, and a table when it goes to a terminal; `--output json` or `--output table` chooses. Amounts keep every digit the API sends.
- A command exits 0 when it succeeded, 1 when the call failed, 2 when the command line is wrong, 3 when the token is missing or refused or lacks the capability the command needs, before the command reads anything, and 4 when a threshold you set was exceeded, as `costfluent estimate submit --fail-above` sets one.
- An error goes to stderr: the API's error as JSON, with its status, code, detail and trace id, when stderr is not a terminal, and one line when it is.
- A list returns one page, as the tool does; the result names the next page, which `--page` reads.
- Every command counts against the token's request limit, and waits out a short limit as the MCP server does.
- A table writes control characters in names, titles and tag values as escapes, because your users and your cloud accounts write them and a terminal would act on them.
- No command prints the token, and `costfluent mcp install` writes it into a file only with `--embed-token`, which needs `--write`.
- `--write` keeps every other server in the file and refuses a file that already has a `costfluent` entry unless `--force` replaces it. It leaves a file it cannot read as plain JSON, such as one with comments, unchanged.

## Related

- [MCP server](/developers/mcp)
- [API authentication](/api/authentication)
- [API reference](/api)
