MCP middleware that governs AI agent spending through a policy engine. Exposes paid tools from a catalog as ordinary MCP tools, running each call through a full governance pipeline: policy check → human approval if over threshold → x402 payment (simulate or testnet) → task execution. Agents never touch wallets; governance is mandatory.
SpendVeto MCP server has critical definition gaps. Tool 1 (spendveto_status) has a good description (145 chars) but empty input schema {}. Tool 2 (dynamic_paid_tools) is dynamically registered from /api/catalog with no visible schema definitions in the source code, tool existence is inferred, not explicitly defined. Neither tool has parameter descriptions. The dynamic tool registration pattern obscures schema visibility entirely. No input validation, no error recovery guidance, no tool annotations. Descriptions exist but lack actionable parameter constraints.
Dynamically registered tools from /api/catalog. Each tool costs a specified amount in USDC per call, paid from the agent's wallet and governed by its SpendVeto spend policy. Hard limits apply, and prices above the policy threshold pause for human approval on the dashboard. A call may come back blocked or denied.
Free. Reports this agent's wallet address, balance, spend policy, last-hour spend, pending approvals, and any delegated budgets. Use it to check whether a paid call would be allowed before making it.
No input schemas visible for either tool. spendveto_status has {} (empty object), dynamic_paid_tools schema is not defined in source, only inferred from /api/catalog fetch.
dynamic_paid_tools is registered dynamically from an external API endpoint. Tool definitions are not visible in the MCP server source code, making it impossible to verify schema, parameter types, or descriptions at registration time.
spendveto_status description lacks parameter guidance. It returns wallet address, balance, policy, spend, approvals, delegations, but the tool has no input parameters documented. Users cannot understand what inputs (if any) control the output.
Error handling is minimal. dynamic_paid_tools can return blocked/denied results, but the error response structure is hardcoded text without categorization (retryable vs fatal). No recovery guidance beyond 'report to user'.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | F | 43 | <=2025-11-25 | v2 |
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). spendveto_status is read-only; dynamic_paid_tools is destructive (spends USDC). These should be explicitly marked.