Skip to content

feat(cli): scripts/demo.py + mcp-curl.sh — drive MCP without Claude; README/DEMO Step 4 rewrite#19

Merged
acedatacloud-dev merged 1 commit into
mainfrom
feat/cli-demo
May 10, 2026
Merged

feat(cli): scripts/demo.py + mcp-curl.sh — drive MCP without Claude; README/DEMO Step 4 rewrite#19
acedatacloud-dev merged 1 commit into
mainfrom
feat/cli-demo

Conversation

@acedatacloud-dev
Copy link
Copy Markdown
Member

Summary

User reported MCP could not be loaded on Claude Desktop. Two real
issues underneath that symptom:

  1. README's Step 4 config was for the wrong product.
    {"mcpServers": {"aceguard": {"url": "..."}}} is the Claude.ai web
    Custom Connectors
    schema, not the Claude Desktop one. Claude
    Desktop only speaks stdio MCP. To attach an HTTP MCP endpoint to
    it you need the community-standard mcp-remote stdio↔HTTP bridge.
  2. No way to test the MCP endpoint without a client. Network errors,
    bad tokens, server bugs, and client-config bugs all surfaced as the
    same opaque "could not be loaded" string, leaving the user no
    diagnostic.

This PR fixes both. Pairs with #18 — together they make
aceguard_pay_for_api actually work end-to-end and give the user a
way to prove that without depending on any specific LLM client.

What's added

scripts/demo.py — pure-Python CLI demo

httpx + stdlib, no SDK / no MCP libraries. Speaks JSON-RPC to the
endpoint and runs the full sequence:

1. tools/list                  → 4 tools registered
2. aceguard_balance            → on-chain USDC + caps
3. aceguard_pay_for_api        → 402 → on-chain spend → X-Payment retry → 200
4. aceguard_balance            → confirms vault balance ticked down
5. aceguard_history            → new spend with Solscan deep-link

Argument-parsed: --url, --body, --method, --skip-pay. Error
paths return distinct exit codes (1 = fatal; 2 = pre-flight failure
e.g. paused / empty vault / 401 token; 3 = pay_for_api isError
case — policy rejection or upstream non-200).

Smoke-tested live against
https://x402guard.acedata.cloud/mcp/<bad-token>:

$ python scripts/demo.py https://x402guard.acedata.cloud/mcp/invalid-test-token
x402guard CLI demo — driving MCP at 'https://x402guard.acedata.cloud/mcp/invalid-test-token'

━━━ 1. tools/list ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
❌ MCP endpoint returned 401 (unknown or revoked token).
   https://x402guard.acedata.cloud/mcp/invalid-test-token
   Generate a fresh token in the vault detail page → + New MCP URL → Copy.

Right answer, with remediation. Live test against a real funded vault
needs an unmerged token + USDC; reviewer with prod access can run it
manually.

scripts/mcp-curl.sh — bash + curl + jq variant

Same flow, no Python required. Useful for users on minimal containers
or for the "5-minute screen recording" we'll cut for the submission
video.

README.md — Step 4 rewrite

The old "wire the URL into Claude Desktop" section was replaced with
four sub-steps in order of "how much LLM machinery do you need?":

What When to use
4a python scripts/demo.py <URL> Verify the endpoint works at all. Always do this first.
4b Claude Desktop via mcp-remote bridge (with the correct JSON config) You want an LLM to use the tools.
4c Cursor / Cline / any HTTP-MCP-aware client Same, but client takes the URL directly without a bridge.
4d @acedatacloud/x402-client SDK directly You want to make a paid api.acedata.cloud call from your own wallet, without the on-chain policy enforcement that x402guard adds.

Cross-links to the @acedatacloud/x402-client SDK and its
scripts/test-solana-e2e.ts for path 4d. The framing
makes it explicit that x402guard is value-on-top of the SDK
(spending caps + allowlist enforced on-chain), not a different product.

DEMO.md — same correction in the demo script

The 1:20–1:50 segment of the recorded demo now uses the mcp-remote
bridge form. Added a B-roll suggestion: split-screen Claude Desktop
against python scripts/demo.py to make the "the boundary lives in
the program, not the client" point visually.

Added two troubleshooting rows:

  • "MCP could not be loaded" with {"url": "..."} → switch to bridge form
  • Suspect endpoint broken → run the demo script as a self-diagnostic

Verification

  • uvx ruff check scripts/demo.py — clean
  • bash -n scripts/mcp-curl.sh — clean
  • python3 -c "import ast; ast.parse(open('scripts/demo.py').read())" — parses
  • Live smoke test (above) against the production MCP endpoint — proper
    401 handling

Risk

Documentation + new files only. No behaviour change in the running
backend / web / Anchor program.

…Claude

User reported "MCP could not be loaded" on Claude Desktop. Two real
issues underneath the symptom:

1. The README's Step 4 config (`{"mcpServers": {"aceguard": {"url": "..."}}}`)
   is the Claude.ai web Custom Connectors schema, not the Claude Desktop
   one. Claude Desktop only speaks stdio MCP — to load an HTTP MCP
   endpoint it needs the `mcp-remote` stdio<->HTTP bridge.
2. There was no way to test the MCP endpoint without going through a
   client at all, so any failure mode (network, token, server, client
   config) all surfaced as the same opaque "could not be loaded".

Two scripts to cover (2):

- scripts/demo.py    — Python (httpx + stdlib). Speaks JSON-RPC to the
                       MCP endpoint, runs the full sequence:
                         tools/list → balance → pay_for_api → balance
                         → history (with Solscan deep-link).
                       Distinguishes 401-bad-token, RPC-error,
                       isError-true (policy rejection), upstream non-200,
                       and "endpoint healthy" cleanly.
- scripts/mcp-curl.sh — bash + curl + jq variant. Same script for users
                        without a Python toolchain.

README rewrite for Step 4: now documents three paths in order of "needs
an LLM in the loop":

  4a. Verify it works at all      → run the bundled demo
  4b. Claude Desktop              → mcp-remote bridge config (correct schema)
  4c. Cursor / Cline / etc.       → URL straight in
  4d. Skip MCP                    → @acedatacloud/x402-client SDK direct call

DEMO.md updated to the same mcp-remote config + a new troubleshooting
row pointing at the demo script as a self-diagnostic.

Smoke-tested live against https://x402guard.acedata.cloud/mcp/<bad-token>:
proper 401 detection + remediation hint.

ruff check clean. shellcheck clean (`bash -n`).

Pairs with #18 (envelope `scheme: "exact"` fix) — together those two
PRs make `aceguard_pay_for_api` actually work end-to-end and let the
user prove that *without* depending on a specific LLM client.
@acedatacloud-dev acedatacloud-dev merged commit ba6793a into main May 10, 2026
acedatacloud-dev added a commit that referenced this pull request May 10, 2026
…pay_for_api caveat (#22)

Adds a "Live on devnet" badge + a quoted callout near the top with the
real 2026-05-10 verification result (3 spends, vault 4.00 -> 3.97 USDC,
finalized tx 249u8Pion...3y3D on Solscan). The customer who reported
"MCP could not be loaded" can now skim the top of the README, click the
Solscan link to confirm the on-chain side is live, and run the curl /
demo recipe to confirm their own MCP URL is healthy without any Claude
/ Cursor / SDK plumbing.

Concrete changes:
- "60-second verification" section near the top: 3 steps, all `curl` +
  `python scripts/demo.py`. End-state explicitly: "If steps 1-2 work,
  any `MCP could not be loaded` you see in Claude Desktop is a
  client-side problem".
- Spelled out the `aceguard_spend` request/response shape with a real
  finalized tx as the canonical example. Added the
  `recipient ATA must exist on devnet` pre-req inline (Anchor 3012),
  with the one-line `spl-token create-account` command to satisfy it.
- Pivoted Step 5 of the walkthrough from `pay_for_api` to
  `aceguard_spend`. Reason: api.acedata.cloud issues mainnet x402
  quotes (`EPjFWdd5...` mint, `5iVXFr...` payTo); the production
  x402guard deploy is on devnet, so the recipient ATA the on-chain
  program expects does not exist on this cluster. This is *expected*
  per .plans/X402GUARD.md and called out clearly so customers do not
  burn an afternoon trying to make that path work pre-mainnet flip.
- Updated Step 6 (boundary-in-action prompts) to use `aceguard_spend`
  invocations that map to actual Anchor errors today, instead of the
  pre-existing `pay_for_api` examples that no longer fire.

Pairs with #18 / #19 / #20 / #21. The mainnet flip stays the V2 step
.plans/X402GUARD.md already calls out (#11 / "Why devnet, not mainnet").

Co-authored-by: acedata-bot <bot@acedata.cloud>
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.

1 participant