Model Context Protocol (MCP) server for Shieldz. Let AI agents accept non-custodial crypto payments and sell digital products with no API key: keyless payment links, tip jars, and pay-to-unlock.
Shieldz MCP has well-structured tool definitions with clear action verbs, good descriptions, and comprehensive input schemas using Zod validation. All 7 tools follow verb_noun naming convention (create_*, get_*, list_*). Descriptions are explicit about functionality, prerequisites, and non-custodial settlement behavior. Input schemas are properly typed with enums, regex patterns, and numeric constraints. However, output schemas are not documented in the tool descriptions, and error handling guidance is minimal. The server does not implement tool annotations (readOnlyHint, destructiveHint, idempotentHint), missing an opportunity to guide LLM behavior on state-modifying operations. Per-tool analysis: all 7 tools score 70+ on naming (clear action verbs) and 75+ on descriptions (50 - 300 chars, LLM-optimized), but schema documentation in descriptions is absent, and output structure is not described to guide downstream chaining.
Create a Shieldz crypto payment invoice (requires an API key). Returns a pay_url to send the customer to. Non-custodial: funds settle to the merchant's own wallet.
Create a one-time crypto payment link with ZERO setup, no account, no API key. Give a destination wallet address and an amount; get back a shareable pay_url, an embeddable button, and a manage_url. Non-custodial: funds settle directly to the address you provide. Optionally pass an email so the owner can claim a full dashboard later.
Create a reusable 'pay what you want' tip jar with ZERO setup, no account, no API key. The payer chooses the amount. Returns a shareable /tip url, an embeddable button, and a manage_url. Idempotent per wallet address: calling again updates the same tip jar. Non-custodial: funds settle directly to the address you provide.
Create a keyless pay-to-unlock page with ZERO setup, no account, no API key: sell a file link, license key, or secret text (up to 100,000 chars) for a fixed price. The buyer pays the set USD price straight to your wallet and the payload is revealed on their paid confirmation, never before. Non-custodial. (To deliver an actual uploaded file up to 10MB, use the multipart HTTP API at /api/v1/unlocks.) IMPORTANT: confirm with your principal that the content is not adult, illegal, or non-consensual, then pass aup_accepted=true, otherwise the call fails.
Output schemas not documented. Tool descriptions do not specify what fields are returned or their types. LLMs cannot plan downstream calls without knowing what create_payment_link returns (e.g., does it return pay_url, manage_url, button_html?). This forces guessing and wastes context on probing.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). create_payment_link, create_tip_jar, create_unlock, and create_invoice are write operations that modify state, but the LLM has no hint that they are destructive or have side effects. get_account_status, get_invoice, and list_invoices are read-only but unmarked. This misses the current spec opportunity (MCP 2026-07-28) to guide LLM behavior on sensitive operations.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | B | 72 | 2026-07-28+ | v2 |
Look up a keyless Shieldz account by its manage_token (returned when you create a payment link, tip jar, or unlock). Returns settlement details, tip jars, totals (paid/pending), and the full invoice list as structured JSON.
Retrieve a Shieldz invoice by its id, including current status (pending/paid/expired/failed).
List recent Shieldz invoices, newest first.
Error handling does not guide LLM recovery. The call() function throws generic errors ('HTTP 400', parsed error messages from backend). No recovery guidance, e.g., if create_payment_link fails with 'invalid chain', the error does not suggest 'must be one of: base, arbitrum, optimism, polygon, ethereum' or 'did you mean: arbitrum?'. Agents cannot self-correct.
get_account_status description is minimal (68 chars). Does not explain what 'settlement details', 'tip jars', 'totals (paid/pending)', 'full invoice list' are or what fields they contain. A 50 - 200 character description optimized for LLM selection would state: 'Retrieve account status for a manage_token: wallet settlement details, associated tip jars, total amounts paid and pending, and full invoice history as structured JSON. Used to reconcile payments and track account activity.'
get_invoice and list_invoices have minimal descriptions (59 and 52 chars). 'Retrieve a Shieldz invoice by its id' does not explain context: when should the LLM call this? What does it do vs create_invoice? A better description: 'Retrieve a Shieldz invoice by ID, including payment status (pending/paid/expired/failed), payer email, amount, and settlement details. Use after create_invoice to poll payment completion.'
No idempotency documentation. create_tip_jar states it is 'idempotent per wallet address', but create_payment_link and create_unlock do not declare idempotency. create_invoice accepts an optional 'idempotency_key' parameter, but the description does not explain what it does or when to pass it. This guidance is critical, agents retry on ambiguous failures, and non-idempotent tools risk duplicate side effects.
Response field naming not aligned with input parameters. create_payment_link returns a 'pay_url', 'embeddable button', and 'manage_url', but these field names are not visible in the source, only inferred from the description. If the response uses 'pay_link' instead of 'pay_url', or 'button_html' instead of 'button', the LLM must discover this through trial and error. Responses should use consistent naming: if the input is 'address', the output should include 'address' or 'settlement_address' (not 'wallet_address' or 'recipient'). Mismatch forces field mapping reasoning.
No pagination documentation for list_invoices. The tool accepts 'limit' (1 - 100) and 'starting_after' parameters, but the description does not explain pagination semantics. Does 'starting_after' return the next 'limit' items after a cursor? Is it a timestamp or ID? Without this clarity, agents cannot construct correct pagination queries. Add: 'Returns up to 'limit' invoices (default 20, max 100), ordered newest first. Pass the id of the last item as starting_after to fetch the next page.'