Skip to content

docs: document the 0.5.0 CLI and gateway behaviour that agents trip on - #19

Open
GigaHierz wants to merge 2 commits into
mainfrom
docs/cli-workarounds
Open

GigaHierz wants to merge 2 commits into
mainfrom
docs/cli-workarounds

Conversation

@GigaHierz

Copy link
Copy Markdown
Contributor

Why

Four bug reports (#4, #6, #7, #8) describe CLI and gateway behaviour on @celo/buy@0.5.0 that this repository cannot fix — the code lives upstream, and each has an upstream issue now. But every one of them costs an agent a failed call, a wrong remedy, or a lost result today, and the skill is what agents read. This PR documents the current behaviour and the working alternative so the guidance matches the shipped release, without changing any rule about money.

Finding Where Today Documented workaround
mcp install --client all fails and prints no error (#4) README, skill primary MCP path unreachable claude mcp add buy -- npx --yes @celo/buy@0.5.0 mcp serve; MCP is optional, the CLI does everything
buy whoami --jsonunknown option (#6) README, skill agent loses its parseable output global flags go before the subcommand
buy skills exits with no output (#4, #6) README, skill agent has nothing to reason about not the discovery path; use the skill's endpoints
--verbose prints nothing on a paid curl (#7) skill agent expects the tx hash there it is in the JSON response; the tee pattern is the record
amount_exceeds_max compares 16753 (atomic) with 0.0001 (decimal) (#6) skill agent may raise the cap by the wrong order of magnitude details.* are atomic; convert first, never exceed approval
poll result contains raw \n inside a JSON string (#7) skill JSON.parse, jq, strict Python all reject the paid result read as text, parse tolerantly or regex out result; the VM and payment are fine
buy send needs CELO; pre-flight refusal says "may still have gone through", exit 0 (#8) README, skill a wallet funded per the docs cannot move its balance out; an agent hunts for a tx that was never sent fund like a float; sending out needs CELO; on 0.5.0 that refusal was never broadcast

What

  • README.md: MCP-optional note and the claude mcp add fallback; the CELO-to-send caveat next to the funding instructions; a two-sentence note on flag placement and buy skills.
  • skills/order-compute/SKILL.md: the same buy send and MCP-fallback text in "Prepare the user's wallet"; a four-bullet list at the top of "Buy through the CLI"; the tolerant-parse note on the scriptStatus: "done" bullet.

Nothing in the quote / approve / never-blindly-retry rules changes.

Verification

Refs #4, #6, #7, #8 — these stay open until the upstream fixes ship; each has a comment pointing at the upstream issue.

🤖 Generated with Claude Code

- MCP server is optional; `claude mcp add` fallback when `mcp install`
  fails without printing its error.
- Global flags must precede the subcommand.
- `buy skills` exits 1 silently and is not the discovery path.
- `--verbose` prints nothing on a paid curl; the tee'd JSON is the record.
- `amount_exceeds_max` compares atomic to decimal; details are atomic.
- Poll `result` can carry raw newlines that strict JSON parsers reject.
- `buy send` needs CELO for gas, so a stablecoin-only wallet cannot move
  its balance out; on 0.5.0 the refusal is misreported as possibly sent.

Refs #4, #6, #7, #8. Upstream fixes tracked in celo-org/cpay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@olanrewajuakeem

Copy link
Copy Markdown

Reporter of #4 here. Two things, one of which I think is the root cause of the mcp install bug rather than a workaround for it.

1. The documented fallback has the same unstated prerequisite as the thing it works around.

I tried to verify the pinned fallback and got:

$ claude mcp add buy -- npx --yes @celo/buy@0.5.0 mcp serve
zsh: command not found: claude

Claude Code is installed here as the VS Code extension, which does not put a claude binary on PATH. That is very likely also why mcp install --client all failed in the first place: it reported could not register with claude, codex (see error above) and printed nothing above it, which is exactly what a swallowed ENOENT from spawning claude would look like.

If that is right, the interesting fix is not the fallback command but the error text — could not find the 'claude' CLI on PATH; install it or edit the config directly would have ended my investigation in seconds rather than sending me to claude mcp add --help, which I also could not run. Worth saying in the docs that both routes need the CLI on PATH, and pointing at the config file for hosts that only have the extension.

2. buy curl returning a non-402 response as success is still undocumented here.

I understand the real fix is CLI-side in cpay#135, but this PR already documents other known-broken 0.5.0 behaviours as interim workarounds — --verbose printing nothing, buy skills exiting silently, amount_exceeds_max mixing units. The same treatment would help here, because it is the one where an agent cannot tell that anything went wrong:

$ npx --yes @celo/buy -s curl https://cpay-demo.onrender.com/quote/AAPL
<!DOCTYPE html> … This service has been suspended by its owner.

Exit 0, no warning, no indication the response was never a 402. Until the CLI validates this, one line telling agents to check the response body is the shape they expected — rather than trusting the exit code — would close the gap.

@viral-sangani viral-sangani left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent is right and five of the seven behaviours check out against @celo/buy@0.5.0. Three things need fixing before merge; two of them are statements in the skill that don't match the shipped code.

1. This edits a mirrored file with no upstream counterpart

cpay's .github/workflows/skill-drift.yml does diff -u between this repo's skills/order-compute/SKILL.md and cpay's .claude/skills/order-compute/SKILL.md and fails on any difference; the sync procedure it prints is cp. This repo's main is byte-identical to cpay commit 96f7b43 (#116). None of this PR's text exists in cpay main (I grepped for "Global flags go before", "mixes units", "parse it tolerantly", "claude mcp add" — zero hits). So the next sync deletes all of it. #15, #17 and #18 also edit this file and have the same problem, and cpay#74 (open) makes the same "MCP is optional" change upstream.

The check is already failing — the last five scheduled runs (2026-09-11 → 09-15) are all red — because cpay main documents /google/* and today GET https://usebuy.ai/google/catalog is 404 while /gcloud/catalog is 200. cpay#127 proposes comparing against the release tag. Whichever way that's resolved, this PR needs either an upstream twin or a decision that this repo is now canonical. Otherwise it's a change with a known expiry.

2. buy send does not exit 0

SKILL.md says the pre-flight refusal "exits 0". In the published tarball, dist/commands/send.js:46 handles the failure with exitWith(...) and no code argument; dist/util.js:38 is exitWith(message, code = 1, …)process.exit(1). Same in the v0.5.0 source. I could not find a path that exits 0. The rest of the paragraph is right: sendStablecoin is a plain writeContract with no feeCurrency, so it needs CELO, and the catch wraps every error including viem's pre-broadcast estimate. cpay#134 repeats the exit-0 claim and should be corrected too.

3. The MCP fallback changes scope

mcp install defaults --scope to user (dist/register-integration-commands.js:18) and passes -s user to claude mcp add. The fallback in both files omits it, so it registers at Claude Code's default local scope — only visible from the directory it was run in. It also drops --account/--sandbox forwarding. Suggest:

claude mcp add -s user buy -- npx --yes @celo/buy@0.5.0 mcp serve

4. The raw-newline claim needs a reproduction before it's stated as fact

Every JSON response in examples/demo-server-gce.mjs (the Cloud Run entrypoint) goes through one helper: res.end(JSON.stringify(body)). The only other res.end calls write 'ok' and the landing page. JSON.stringify escapes control characters and emits no trailing newline. The byte dump in #7 has raw 0x0A inside the string and a trailing 0x0A at offset 350 — which is exactly what zsh's builtin echo produces from a string containing \n (it interprets escapes by default and appends a newline). cpay#132 says the concatenation is "likely" somewhere; I couldn't find it. Please reproduce with curl -o poll.json on a multi-line script before the skill tells every agent the gateway emits invalid JSON.

5. Smaller accuracy points

  • buy skills: the silence comes from dist/main.js:26, writeErr: () => {}, which suppresses Commander's help-on-missing-subcommand for every group — I get exit 1 and zero bytes from buy account, buy mcp and buy skills alike. buy skills list works and prints the two demo providers. The README line reads as if the whole skills group is dead; say "bare buy skills prints nothing; buy skills list works but lists only two demo providers on a suspended host." cpay#135's "buy mcp prints help" is not what 0.5.0 does.
  • --verbose: in curl.ts, log() writes to stderr on a TTY and, in JSON mode (any pipe, including the mandatory | tee), collects into diagnostics[] which is attached to the failure envelope as details.log. So it prints nothing on success when piped; on failure the quote and signing lines are in the envelope. Worth saying, since that's when an agent needs them. The tx hash is never logged. The two canonical examples (lines 260, 304) still pass --verbose curl — either remove it or say why it's kept.
  • Verification section: PR #10's script checks version pins, URL prefix, the machine-type table, and tokens. It doesn't exercise anything in this PR, so "passes on this branch" doesn't support the claims.

Confirmed correct

  • Global flags before the subcommand: dist/main.js:46 enablePositionalOptions(), deliberate, with a comment saying so. The list of affected flags is exact.
  • amount_exceeds_max message uses the raw --max-amount string against the atomic challenge amount; details.required and details.maxAmount are both atomic (curl.ts, the cap check).
  • mcp install prints "see error above" with nothing above when the claude/codex spawn itself fails: runOnce in commands/mcp.js resolves false on the child's error event without writing anything.
  • The quote / approve / never-retry rules are untouched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants