> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metalntwx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# The metal-agent Skill

> Set up and use the metal-agent CLI to pay x402 sellers on Metal: install it, create or import the agent's key, fund it, pay a URL that answers 402, pay under a mandate from an account the agent does not own, and look up a settlement. Use when a task needs an agent to pay for an HTTP resource in *USDZ on Metal, to pay with a mandate credential the user gives it, to set up metal-agent, or to check what a Metal payment settled.

`metal-agent` is the CLI and MCP server of the `@metalntwx/metal-agent` npm
package. It pays x402 sellers in `*USDZ` on Metal: an agent requests a URL,
the seller answers 402 with a price, and `metal-agent` signs the payment and
returns the resource with the settlement.

Every command that touches the chain takes `--network testnet`. Metal's
demo seller is at `https://x402-demo-testnet.metalntwx.com/quote` and the
block explorer at `https://testnet.metalntwx.com`.

`key`, `fund` and `pay` need no RPC endpoint. `status`, `account create` and
the `mandate` commands other than `credential create` need the user's own: ask for it, unless `METAL_RPC_URL` is already
set, and pass it with `--rpc-url`. A user without one creates it as
`https://docs.metalntwx.com/network/endpoints` describes.

The key file defaults to `~/.metal-agent/key.json`. It holds the private key
in the clear, owner-readable only; treat the file as the secret.

If `metal-agent --version` prints `0.3.3` and the user already has a key,
go to the command you need. Otherwise start with Setup.

## Setup

Run the steps in order. After each step, show the user its output.

1. Check Node.js with `node --version`. If it is missing, tell the user to
   install Node.js, and stop.

2. Install the CLI, or move an installed one to this version:

   ```bash theme={null}
   npm install -g @metalntwx/metal-agent@0.3.3
   ```

   Then `metal-agent --version` prints `0.3.3`. If it prints anything else,
   another copy comes first on the `PATH`: tell the user, and stop.

3. Install this skill, so that later sessions have it without fetching this
   page. Copy the directory
   `$(npm root -g)/@metalntwx/metal-agent/skills/metal-agent` into your
   skills directory. In Claude Code that is `.claude/skills/metal-agent` in
   the project, or `~/.claude/skills/metal-agent` for every project. If your
   agent has no skills directory, skip this step.

4. Connect the MCP server, which serves `pay`, `fund` and `status` as tools.
   In Claude Code:

   ```bash theme={null}
   claude mcp add metal-agent -- npx -y @metalntwx/metal-agent@0.3.3 mcp
   ```

   In any other MCP host, add this server to its MCP configuration:

   ```json theme={null}
   {
     "mcpServers": {
       "metal-agent": {
         "command": "npx",
         "args": ["-y", "@metalntwx/metal-agent@0.3.3", "mcp"]
       }
     }
   }
   ```

   The server pays with the default key file. If the user's key is in
   another file, or the agent pays under a mandate, add `--key <file>` or
   `--mandate <credential>` after `mcp`. Under a mandate on testnet, also set
   the user's endpoint in the server's environment, or in an `env` block in
   another host's configuration:

   ```bash theme={null}
   claude mcp add metal-agent -e METAL_RPC_URL=<endpoint> -- npx -y @metalntwx/metal-agent@0.3.3 mcp --mandate <credential>
   ```

   The tools appear after the host reloads. If you cannot reload it, tell
   the user to, and carry on with the CLI.

5. Get the agent's key. Ask the user whether they have a funded key file. If they do,
   ask for its path and run `metal-agent key import <path>`. Otherwise run
   `metal-agent key create`. Tell the user the address. If either answers
   that the key file already holds a key, tell the user the address in its
   `address` field, and ask whether to pay with that key.

6. Fund the key from the testnet faucet, unless the user said it is funded,
   and show the user the `transaction` and `explorer` lines:

   ```bash theme={null}
   metal-agent fund <address> --network testnet
   ```

   If the faucet refuses, show the user its error and what it says to do.

7. Pay Metal's demo seller once and show the user the `transaction` and
   `explorer` lines:

   ```bash theme={null}
   metal-agent pay https://x402-demo-testnet.metalntwx.com/quote --network testnet
   ```

8. Tell the user what is set up: the CLI, the skill and where you put it,
   the MCP server, the key's address and file, and the payment's explorer
   link.

## Stop and ask the user first

* Before you pay a URL the user did not name. The demo seller in Setup is
  the one exception.
* Before you pass a `--max` above the default of 1 `*USDZ`.
* Before you import a key, or write a key anywhere but the default file.
* Before you run `account create`, `mandate credential create`, `bind`,
  `replace`, `renew` or `revoke`. They are the account owner's to run.

Never print a private key or the contents of a key file. Never move, copy
or delete a key file. If the owner's key file is within your reach, tell
the user; the limits bind you only while it is not.

## key create

Generate the agent's key and write its key file. Prints the address
and the key file's path. It does not overwrite an existing key file; `--out` writes
another.

```bash theme={null}
metal-agent key create --out ./key.json
```

## key import

Load a key someone funded for you. Takes a hex key (with or without `0x`),
a file holding one, or a key file. Prints the address, never the key.

```bash theme={null}
metal-agent key import ./funded-key.txt
```

## fund

Ask the public faucet for `*USDZ`; the faucet sets the amount.
An address that already holds a full grant is reported as funded. A grant
prints `transaction <hash>` and `explorer <url>`, the transaction's page on
the block explorer. The faucet limits how often one client can ask: on a
`client_rate_limited` error, wait the seconds it names; on
`client_daily_limit_reached`, fund the address from the Faucet page in the
Metal console the error names; on either daily-limit error, load a
pre-funded key with `key import`.

```bash theme={null}
metal-agent fund <address> --network testnet
```

## pay

Request a URL. If it answers 402 with an `exact` offer in `*USDZ` on the
named network, sign the authorization with the key file, retry, and print
the settlement, the response headers and the body.

```bash theme={null}
metal-agent pay https://x402-demo-testnet.metalntwx.com/quote --network testnet --key ./key.json
```

Read `transaction <hash>` from the output for the settlement,
`header <name>: <value>` lines for the response headers, and `body …` for
the resource. `explorer <url>` is the settlement's page on the block
explorer; hand it to the user.

`pay` refuses any offer above `--max` before it signs. The limit is 1
`*USDZ` per payment by default. Pass a larger `--max` only when the user
agreed to that price.

```bash theme={null}
metal-agent pay https://example.com/report --network testnet --max 5
```

For a request other than GET, pass `--method` (POST when there is a body),
`--body` (sent as `application/json` unless a `--header` names a content
type) and repeated `--header 'name: value'`; `pay` sends them again with the
payment. To buy RPC over x402, POST the JSON-RPC call to
`https://rpc-testnet.metalntwx.com/v1`:

```bash theme={null}
metal-agent pay https://rpc-testnet.metalntwx.com/v1 --network testnet --method POST --body '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'
```

The output's `header rpc-aas-endpoint: <url>` is the endpoint that payment
bought. Send further JSON-RPC calls to that URL with a plain HTTP POST, with
no payment, until it answers 402 again; do not pay at the RPC URL again for
each call.

A refusal exits 1 with the reason:

* `error: payment refused before signing: offer of … is above the … limit per payment; pass --max to allow it`
  means the offer is above the limit.
* `error: payment refused before signing: offer is in …, not *USDZ …` or
  `offer signs for the EIP-712 domain …` means the offer asks for something
  other than `*USDZ`. Tell the user; a larger `--max` does not change it.
* `error: payment refused: <reason>` means the facilitator refused at
  verification, or the seller's own check refused; nothing was paid.
* `error: payment refused at settlement: <reason>` means the facilitator
  refused at settlement for a reason that moves nothing, with the
  transaction hash when there is one; nothing was paid. On
  `daily_limit_reached`, retry later.
* On `error: payment in doubt at settlement: …`, or any error after the
  `nonce` line that is not a `payment refused` line, such as `fetch failed`
  or an HTTP status from the seller, the payment may have settled. Do not
  pay again. Tell the user, with the `nonce` line `pay` printed; when the
  error names a transaction hash, `status` on it shows whether it landed.

## `pay --mandate`

When the user gives you a mandate's credential file, pay with it in place
of a key. The payment comes from the owner's account, within limits the
owner set and Metal enforces: the most per payment, a total budget and,
when the mandate names them, the payees.

```bash theme={null}
metal-agent pay https://example.com/report --mandate ./mandate.json --network testnet
```

`pay` signs with the mandate key beside the credential, `mandate.key.json`
for `mandate.json`; `--mandate-key-file <file>` names it when it is
elsewhere. Never show the key file's contents. `--max` and `--key` do not
apply. A payment outside the mandate exits 1 with
`error: payment refused: <reason> (…)`, a reason starting `mandate_`. Tell
the user which limit it was; only the owner can change a mandate. After
`mandate_revoked`, ask the user for the credential that replaced it, and
pay with that. If the error says no key beside the credential is this
mandate's, ask the user where the key file is, and pass it as
`--mandate-key-file`.

A seller that asks for your signed order gets it with the payment, and
`pay` prints it as `signed-offer <text>`. Keep it, and pass it to
`mandate verify --credential <file> --issuer <file> --tx <hash> --signed-offer <text> --network testnet --rpc-url <endpoint>`
to check the two together.

`mandate status` shows what is left: read `spent <amount> of <budget>`,
`revoked yes|no` and the `payee` lines.

```bash theme={null}
metal-agent mandate status --credential ./mandate.json --network testnet --rpc-url <endpoint>
```

`mandate pull` pays an address the mandate allows, with no seller. The
facilitator submits it and pays the gas. Pass `--payment-id` with the id of
the payment it is for, such as the refund's or the invoice's: two payouts of
one amount to one payee, while the first is pending, are two payments
only under two ids. The `outcome`
line says what happened: `paid`, exit 0; `refused, not sent` with `reason`
and `recorded no`, for a pull refused when checked, which sends nothing;
`refused` with `reason`, for one the account refused and recorded;
`refused, not paid`, for a sent pull that can no longer land; `held`,
exit 4, for one the receive policy contract holds in escrow, so the payee was
not paid and the amount counts against the budget: do not send it again;
the payee, or the authority its policy names, releases it; or `pending`,
exit 3. Refusals exit 1. Pass `--record-refusal`, with `--key` naming a
funded key to send it, only when the user asks for the refusal to be
recorded on chain. On `outcome pending`, run the same command again, on the
same machine, with the same credential, `--payment-id`, `--to` and
`--amount`: it sends the same pull, which pays at most once, or reports its
outcome. Never add, drop or change the payment id, or change the amount, to
pay a pending pull again. After `outcome refused`, nothing was paid, and the
same payment id may be sent with other values. `error: the pull was not
sent: …` names why, such as the facilitator's refusal; tell the user.
An error saying `the pull kept before may have been sent and can land until
…` means that pull can still land until then: tell the user, and never pay
it under another payment id. The same holds for an error naming an event the SDK
does not decode: the pull may have paid.

```bash theme={null}
metal-agent mandate pull --credential ./mandate.json --to <payee address> --amount 2.50 --payment-id <payment id> --network testnet --rpc-url <endpoint>
```

`mandate verify` checks the mandate and every payment under it from the
chain alone, or one `--tx <hash>`, and exits 1 when a check fails. Run it
when the user asks whether a payment was within the mandate; read
`verified yes|no`, and a failed `step` line names what differs. A failure
that names a topic the SDK does not decode means metal-agent and the
account may be different versions; tell the user. It needs
the issuer's public key file, the `issuer-public-file` that `credential
create` printed; ask the user for it.

```bash theme={null}
metal-agent mandate verify --credential ./mandate.json --issuer <issuer public key file> --network testnet --rpc-url <endpoint>
```

## status

Show a settlement from chain data: status, block, submitter, gas paid in
`*USDZ`, and the transfer it carried. Pass the endpoint the user gave you;
without one, `status` exits with
`error: testnet needs an RPC endpoint of your own: sign up at https://console-testnet.metalntwx.com, create an endpoint, then pass --rpc-url or set METAL_RPC_URL`.

```bash theme={null}
metal-agent status <transaction hash> --network testnet --rpc-url <endpoint>
```

## mcp

Serve `pay`, `fund` and `status` as MCP tools, each taking a `network`, over
stdio for an MCP host; under a mandate, `mandate_pull`, `mandate_status` and
`mandate_verify` as well. What `pay` and `mandate_pull` spend is set when
the server starts, by whoever starts it: a key file and `--max`, or one
mandate's credential file, which each call reads. `pay` takes the request as
optional `method`, `body` and `headers`, and under a key a `max` that can
only lower the server's limit, and ends a refusal after signing, or a
payment that may have settled, with a `next:` line. `fund` and `status`
take the endpoint as `rpcUrl`, which
`status` on testnet needs unless the server's environment sets
`METAL_RPC_URL`. With the server's `METAL_RPC_URL`, `fund` asks the testnet
faucet nothing for an address that already holds about a full grant. The
mandate tools take no `rpcUrl`: they read the chain through the server's
`METAL_RPC_URL`, or devnet's default endpoint, and fail naming a chain other
than the network's. `mandate_pull` takes `to`, `amount` and a `paymentId`,
one per payment, and ends any outcome but `paid` with a `next:` line;
`mandate_verify` takes the `issuers` to trust. No tool chooses a key, a
mandate or a higher limit, and none sets or changes a mandate.

```bash theme={null}
metal-agent mcp
metal-agent mcp --mandate /path/to/mandate.json
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.