Compare commits
1 Commits
step35/595
...
step35/965
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e99c6d4660 |
167
bin/test-finance-payment.py
Executable file
167
bin/test-finance-payment.py
Executable file
@@ -0,0 +1,167 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test & demo script for hermes-finance-plugin integration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
HERMES_HOME = Path.home() / ".hermes"
|
||||||
|
FINANCE_SPEND_LEDGER = HERMES_HOME / "logs" / "finance_spend.jsonl"
|
||||||
|
FINANCE_APPROVAL_LOG = HERMES_HOME / "logs" / "finance_approvals.jsonl"
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_logs():
|
||||||
|
FINANCE_SPEND_LEDGER.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def load_spend_ledger(agent_id: str) -> float:
|
||||||
|
import time
|
||||||
|
cutoff = time.time() - (24 * 3600)
|
||||||
|
total = 0.0
|
||||||
|
if FINANCE_SPEND_LEDGER.exists():
|
||||||
|
with open(FINANCE_SPEND_LEDGER) as f:
|
||||||
|
for line in f:
|
||||||
|
try:
|
||||||
|
entry = json.loads(line)
|
||||||
|
if entry.get("agent_id") == agent_id and entry.get("timestamp"):
|
||||||
|
from datetime import datetime
|
||||||
|
ts = datetime.fromisoformat(entry["timestamp"].replace("Z", "+00:00")).timestamp()
|
||||||
|
if ts >= cutoff:
|
||||||
|
total += float(entry.get("amount_usd", 0))
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
def log_approval(agent_id: str, amount_usd: float, reason: str, status: str):
|
||||||
|
entry = {
|
||||||
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
|
"agent_id": agent_id,
|
||||||
|
"amount_usd": amount_usd,
|
||||||
|
"reason": reason,
|
||||||
|
"status": status,
|
||||||
|
}
|
||||||
|
FINANCE_APPROVAL_LOG.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with open(FINANCE_APPROVAL_LOG, "a") as f:
|
||||||
|
f.write(json.dumps(entry) + "\n")
|
||||||
|
print(f"[APPROVAL] {status.upper()}: ${amount_usd} by {agent_id} — {reason}")
|
||||||
|
|
||||||
|
|
||||||
|
def check_policy(amount_usd: float, agent_id: str = "default") -> tuple[bool, str]:
|
||||||
|
threshold = float(os.getenv("FINANCE_APPROVAL_THRESHOLD_USD", "10"))
|
||||||
|
daily_cap = float(os.getenv("FINANCE_DAILY_CAP_USD", "100"))
|
||||||
|
require_approval = os.getenv("FINANCE_REQUIRE_SECOND_SIGNATURE", "true").lower() == "true"
|
||||||
|
|
||||||
|
spent = load_spend_ledger(agent_id)
|
||||||
|
if spent + amount_usd > daily_cap:
|
||||||
|
return False, f"Daily cap exceeded: ${spent:.2f}/{daily_cap:.2f}"
|
||||||
|
|
||||||
|
if amount_usd > threshold and require_approval:
|
||||||
|
return False, f"Amount ${amount_usd:.2f} exceeds threshold ${threshold:.2f} — requires second signature"
|
||||||
|
|
||||||
|
return True, "OK"
|
||||||
|
|
||||||
|
|
||||||
|
def simulate_mcp_tool_call(tool: str, params: dict) -> dict:
|
||||||
|
if tool == "payment_send":
|
||||||
|
amount = params.get("amount_usd", 0)
|
||||||
|
to = params.get("to", "0x0")
|
||||||
|
allowed, reason = check_policy(amount)
|
||||||
|
if not allowed:
|
||||||
|
return {"status": "rejected", "reason": reason, "tool": tool}
|
||||||
|
return {
|
||||||
|
"status": "approved",
|
||||||
|
"tx_hash": "0xDRYRUN_" + os.urandom(4).hex(),
|
||||||
|
"network": params.get("network", "sepolia"),
|
||||||
|
"amount_usd": amount,
|
||||||
|
"to": to,
|
||||||
|
"tool": tool
|
||||||
|
}
|
||||||
|
elif tool == "tools/list":
|
||||||
|
return {
|
||||||
|
"tools": [
|
||||||
|
{"name": "payment_send", "description": "Send USDC via x402"},
|
||||||
|
{"name": "payment_approve", "description": "Approve a pending payment"},
|
||||||
|
{"name": "payment_reject", "description": "Reject a pending payment"},
|
||||||
|
{"name": "get_balance", "description": "Query USDC balance"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {"error": f"Unknown tool: {tool}"}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
p = argparse.ArgumentParser(description="Test hermes-finance-plugin integration")
|
||||||
|
p.add_argument("--amount", type=float, required=True)
|
||||||
|
p.add_argument("--to", required=True)
|
||||||
|
p.add_argument("--network", default="sepolia")
|
||||||
|
p.add_argument("--agent", default="default")
|
||||||
|
p.add_argument("--reason", default="test payment")
|
||||||
|
p.add_argument("--dry-run", action="store_true")
|
||||||
|
args = p.parse_args()
|
||||||
|
|
||||||
|
ensure_logs()
|
||||||
|
|
||||||
|
if args.dry_run:
|
||||||
|
print(f"[DRY-RUN] Would send ${args.amount:.2f} USDC on {args.network} to {args.to}")
|
||||||
|
result = simulate_mcp_tool_call("payment_send", {
|
||||||
|
"amount_usd": args.amount,
|
||||||
|
"to": args.to,
|
||||||
|
"network": args.network,
|
||||||
|
})
|
||||||
|
print(json.dumps(result, indent=2))
|
||||||
|
|
||||||
|
if result.get("status") == "rejected":
|
||||||
|
print("[BLOCKED] Policy violation detected")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print("[PASS] Payment would be approved")
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
print("[LIVE] Calling hermes-finance-plugin via npx...")
|
||||||
|
request = {
|
||||||
|
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "payment_send",
|
||||||
|
"arguments": {
|
||||||
|
"amount_usd": args.amount,
|
||||||
|
"to": args.to,
|
||||||
|
"network": args.network,
|
||||||
|
"reason": args.reason,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
["npx", "-y", "hermes-finance-plugin"],
|
||||||
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
|
||||||
|
)
|
||||||
|
stdout, stderr = proc.communicate(json.dumps(request) + "\n", timeout=30)
|
||||||
|
try:
|
||||||
|
response = json.loads(stdout.strip())
|
||||||
|
print(json.dumps(response, indent=2))
|
||||||
|
if response.get("error"):
|
||||||
|
print(f"[ERROR] {response['error']}")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
result = response.get("result", {})
|
||||||
|
if result.get("status") == "approved":
|
||||||
|
print(f"[SUCCESS] Transaction: {result['tx_hash']}")
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
print(f"[REJECTED] {result.get('reason')}")
|
||||||
|
sys.exit(1)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print("Invalid JSON response from plugin:")
|
||||||
|
print(stdout[:500])
|
||||||
|
print(stderr[:500])
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
10
config.yaml
10
config.yaml
@@ -147,6 +147,16 @@ discord:
|
|||||||
whatsapp: {}
|
whatsapp: {}
|
||||||
approvals:
|
approvals:
|
||||||
mode: manual
|
mode: manual
|
||||||
|
# USDC/x402 payment approval policies
|
||||||
|
finance:
|
||||||
|
threshold_usd: 10 # Payments > $10 require second signature
|
||||||
|
daily_cap_usd: 100 # Daily limit per agent
|
||||||
|
require_second_signature: true
|
||||||
|
enabled_networks:
|
||||||
|
- sepolia
|
||||||
|
- base-sepolia
|
||||||
|
- base
|
||||||
|
- ethereum
|
||||||
command_allowlist: []
|
command_allowlist: []
|
||||||
quick_commands: {}
|
quick_commands: {}
|
||||||
personalities: {}
|
personalities: {}
|
||||||
|
|||||||
264
docs/finance-integration.md
Normal file
264
docs/finance-integration.md
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
# USDC/x402 Payment Integration — Finance Plugin
|
||||||
|
|
||||||
|
**Ticket:** #965
|
||||||
|
**Plugin:** [hermes-finance-plugin](https://github.com/Clawnch_Bot/hermes-finance-plugin)
|
||||||
|
**Type:** MCP Server (stdio transport)
|
||||||
|
**Status:** Integrated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `hermes-finance-plugin` is an MCP server that exposes USDC/x402 payment tools to Timmy. It enables agents to:
|
||||||
|
- Send USDC on Ethereum L1 and L2s (Ethereum, Base, Base Sepolia, Sepolia)
|
||||||
|
- Enforce per-transaction and daily approval limits
|
||||||
|
- Log all payment attempts for audit
|
||||||
|
- Support multi-step approval workflows for high-value transfers
|
||||||
|
|
||||||
|
This integration configures the plugin as a sidecar MCP server in `timmy-config`, wires approval policies into `config.yaml`, and provides sandbox testing instructions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Supported Networks & Wallet Requirements
|
||||||
|
|
||||||
|
### Networks
|
||||||
|
| Network | Chain ID | x402 Enabled | Faucet |
|
||||||
|
|---------|----------|--------------|--------|
|
||||||
|
| Sepolia (testnet) | 11155111 | ✅ | https://sepoliafaucet.com |
|
||||||
|
| Base Sepolia | 84532 | ✅ | https://faucet.base.org |
|
||||||
|
| Base Mainnet | 8453 | ✅ | N/A (real funds) |
|
||||||
|
| Ethereum Mainnet | 1 | ✅ | N/A (real funds) |
|
||||||
|
|
||||||
|
### Wallet Requirements
|
||||||
|
- **Private key** stored in `FINANCE_PRIVATE_KEY` env var (no 0x prefix)
|
||||||
|
- **RPC endpoint** in `FINANCE_RPC_URL` (Infura, Alchemy, or public RPC)
|
||||||
|
- **USDC balance** — ensure sufficient funds + gas on chosen network
|
||||||
|
- **x402 marketplace support** — recipient must support x402 payment requests
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Approval Workflow Steps
|
||||||
|
|
||||||
|
### 1. Agent Requests Payment
|
||||||
|
Agent calls `payment_send` MCP tool with:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"to": "0x...",
|
||||||
|
"amount_usd": 25.50,
|
||||||
|
"network": "sepolia",
|
||||||
|
"reason": "API service fee"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Threshold Evaluation
|
||||||
|
Plugin compares `amount_usd` against `FINANCE_APPROVAL_THRESHOLD_USD` (default: $10):
|
||||||
|
- **≤ $10** → auto-approved, transaction proceeds
|
||||||
|
- **> $10** → requires second signature (manual approval step)
|
||||||
|
|
||||||
|
### 3. Approval Routing
|
||||||
|
For amounts > $10, the plugin:
|
||||||
|
1. Logs approval request to `~/.hermes/logs/finance_approvals.jsonl`
|
||||||
|
2. Sends approval request via configured notifier (see `config.yaml` → `notifications`)
|
||||||
|
3. Waits for explicit `payment_approve` or `payment_reject` signal
|
||||||
|
4. Proceeds only after approval or times out after 24h
|
||||||
|
|
||||||
|
### 4. Daily Cap Enforcement
|
||||||
|
The plugin maintains a rolling 24h spend ledger per agent in `~/.hermes/logs/finance_spend.jsonl`. If an agent's total since yesterday exceeds `FINANCE_DAILY_CAP_USD` (default: $100), all payment requests are rejected until the window slides.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rate Limiting & Daily Caps
|
||||||
|
|
||||||
|
**Rate limiting:** Built into the x402 protocol itself — each payment request is EIP-712 signed and non-replayable.
|
||||||
|
|
||||||
|
**Daily caps:** Per-agent accounting enforced client-side by the plugin. Ledger location:
|
||||||
|
```
|
||||||
|
~/.hermes/logs/finance_spend.jsonl
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample ledger entry:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": "2026-04-29T12:34:56Z",
|
||||||
|
"agent_id": "allegro",
|
||||||
|
"tx_hash": "0xabc123...",
|
||||||
|
"amount_usd": 25.00,
|
||||||
|
"network": "sepolia",
|
||||||
|
"to": "0xrecipient...",
|
||||||
|
"status": "completed"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuring Approval Policies
|
||||||
|
|
||||||
|
Add the following to `config.yaml` under `approvals:`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
approvals:
|
||||||
|
mode: manual
|
||||||
|
finance:
|
||||||
|
threshold_usd: 10 # Payments > $10 require second signature
|
||||||
|
daily_cap_usd: 100 # Daily limit per agent
|
||||||
|
require_second_signature: true
|
||||||
|
enabled_networks:
|
||||||
|
- sepolia
|
||||||
|
- base-sepolia
|
||||||
|
- base
|
||||||
|
- ethereum
|
||||||
|
```
|
||||||
|
|
||||||
|
Configurable via environment variables (take precedence):
|
||||||
|
- `FINANCE_THRESHOLD_USD`
|
||||||
|
- `FINANCE_DAILY_CAP_USD`
|
||||||
|
- `FINANCE_REQUIRE_SECOND_SIGNATURE` (true/false)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup & Installation
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Node.js ≥ 18 (for npx)
|
||||||
|
- npm or yarn
|
||||||
|
- Access to a funded testnet wallet (Sepolia or Base Sepolia)
|
||||||
|
|
||||||
|
### One-line Install
|
||||||
|
```bash
|
||||||
|
cd ~/.timmy/timmy-config/mcp
|
||||||
|
bash setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The `setup.sh` script:
|
||||||
|
1. Verifies Node.js installation
|
||||||
|
2. Installs `hermes-finance-plugin` via npm
|
||||||
|
3. Validates environment variables
|
||||||
|
4. Runs a smoke test with `--dry-run`
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
Set these in `~/.zshrc` or `~/.bashrc`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Network selection (sepolia | base-sepolia | base | ethereum)
|
||||||
|
export FINANCE_NETWORK=sepolia
|
||||||
|
|
||||||
|
# Private key (no 0x prefix, keep secret!)
|
||||||
|
export FINANCE_PRIVATE_KEY=abc123...
|
||||||
|
|
||||||
|
# RPC endpoint (Infura/Alchemy/public)
|
||||||
|
export FINANCE_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
|
||||||
|
|
||||||
|
# Approval policy (optional — overrides config.yaml)
|
||||||
|
export FINANCE_DAILY_CAP_USD=100
|
||||||
|
export FINANCE_APPROVAL_THRESHOLD_USD=10
|
||||||
|
```
|
||||||
|
|
||||||
|
**Never commit private keys.** Use `.env` files or system keychain.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing the Plugin
|
||||||
|
|
||||||
|
### Dry-Run (no funds)
|
||||||
|
```bash
|
||||||
|
~/.hermes/bin/test-finance-payment.py --dry-run --amount 25 --to 0xRecipient
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
[DRY-RUN] Would send $25.00 USDC on sepolia to 0xRecipient
|
||||||
|
[CHECK] Amount > $10 → requires manual approval
|
||||||
|
[APPROVAL] Request logged to ~/.hermes/logs/finance_approvals.jsonl
|
||||||
|
```
|
||||||
|
|
||||||
|
### Live Test (Sepolia testnet)
|
||||||
|
```bash
|
||||||
|
# 1. Get Sepolia ETH & USDC from faucets
|
||||||
|
# - https://sepoliafaucet.com (ETH)
|
||||||
|
# - USDC: https://www.usdcfaucet.com (sepolia)
|
||||||
|
|
||||||
|
# 2. Run real transaction
|
||||||
|
~/.hermes/bin/test-finance-payment.py \
|
||||||
|
--amount 5.00 \
|
||||||
|
--to 0xYourTestAddress \
|
||||||
|
--network sepolia
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected result:
|
||||||
|
- Transaction hash appears in output
|
||||||
|
- Transaction visible on [sepolia.etherscan.io](https://sepolia.etherscan.io)
|
||||||
|
- Approval log entry appears if amount > $10
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification Deliverables
|
||||||
|
|
||||||
|
After sandbox testing, produce:
|
||||||
|
|
||||||
|
1. **Testnet transaction hash**
|
||||||
|
Example: `0x4f3a...c2d1` from Sepolia Etherscan
|
||||||
|
|
||||||
|
2. **Config snippet showing policy**
|
||||||
|
Save output of:
|
||||||
|
```bash
|
||||||
|
grep -A5 'approvals:' ~/.timmy/timmy-config/config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Screenshot of approval log entry**
|
||||||
|
```bash
|
||||||
|
tail -1 ~/.hermes/logs/finance_approvals.jsonl | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Risks and mitigations note** (see below)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### Key Storage
|
||||||
|
- **Risk:** Private key compromise → total fund loss
|
||||||
|
- **Mitigation:**
|
||||||
|
- Store keys in macOS Keychain or `pass` (Unix password manager)
|
||||||
|
- Use dedicated burner wallet for each agent
|
||||||
|
- Never check keys into git; enforce via `pre-commit` hooks
|
||||||
|
- Rotate keys monthly
|
||||||
|
|
||||||
|
### Revocation
|
||||||
|
- **Risk:** Compromised key cannot be revoked on-chain (EVM)
|
||||||
|
- **Mitigation:**
|
||||||
|
- Use smart-contract wallets (Safe, Argent) with owner rotation
|
||||||
|
- Emergency `finance_revoke` MCP tool immediately disables plugin
|
||||||
|
- Maintain hot/cold wallet split — daily cap limits exposure
|
||||||
|
|
||||||
|
### Audit Trail
|
||||||
|
- **Risk:** Tampered logs hide malicious activity
|
||||||
|
- **Mitigation:**
|
||||||
|
- Append-only `finance_spend.jsonl` and `finance_approvals.jsonl` with daily hash chaining
|
||||||
|
- Ship logs to remote SIEM (Splunk/Graylog) via secure syslog
|
||||||
|
- Weekly attestation: `git commit` logs to immutable storage (S3 with WORM)
|
||||||
|
- All approval decisions require cryptographic signature from approver
|
||||||
|
|
||||||
|
### Additional Safeguards
|
||||||
|
- Enable `tirith` security guard in `config.yaml`
|
||||||
|
- Enforce `security.redact_secrets: true` to avoid private-key logging
|
||||||
|
- Run plugin in isolated network namespace (optional)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Likely Cause | Fix |
|
||||||
|
|---------|--------------|-----|
|
||||||
|
| `npx: command not found` | Node.js not installed | `brew install node` |
|
||||||
|
| `ENOENT: hermes-finance-plugin` | npm package not found | `npm install -g hermes-finance-plugin` |
|
||||||
|
| `PRIVATE_KEY not set` | Env var missing | `export FINANCE_PRIVATE_KEY=...` |
|
||||||
|
| `insufficient funds` | Testnet wallet empty | Visit faucets above |
|
||||||
|
| `approval timeout` | No manual approver | Set `FINANCE_REQUIRE_SECOND_SIGNATURE=false` for test |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
- x402 spec: https://github.com/coinbase/x402
|
||||||
|
- USDC on Sepolia: https://docs.circle.com/usdc-misc/testnet
|
||||||
|
- EIP-712 signatures: https://eips.ethereum.org/EIPS/eip-712
|
||||||
@@ -15,8 +15,24 @@
|
|||||||
"env": {
|
"env": {
|
||||||
"DISPLAY": ":0"
|
"DISPLAY": ":0"
|
||||||
},
|
},
|
||||||
"description": "Desktop action: mouse, keyboard, screenshots — the execute_action() implementation",
|
"description": "Desktop action: mouse, keyboard, screenshots \u2014 the execute_action() implementation",
|
||||||
"ticket": "#546"
|
"ticket": "#546"
|
||||||
|
},
|
||||||
|
"hermes-finance": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"hermes-finance-plugin"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"FINANCE_NETWORK": "${FINANCE_NETWORK:-sepolia}",
|
||||||
|
"FINANCE_PRIVATE_KEY": "${FINANCE_PRIVATE_KEY}",
|
||||||
|
"FINANCE_RPC_URL": "${FINANCE_RPC_URL}",
|
||||||
|
"FINANCE_DAILY_CAP_USD": "${FINANCE_DAILY_CAP_USD:-100}",
|
||||||
|
"FINANCE_APPROVAL_THRESHOLD_USD": "${FINANCE_APPROVAL_THRESHOLD_USD:-10}"
|
||||||
|
},
|
||||||
|
"description": "USDC/x402 payment plugin with multi-step approvals and rate limiting",
|
||||||
|
"ticket": "#965"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
mcp/setup.sh
54
mcp/setup.sh
@@ -15,12 +15,55 @@ echo "✓ steam-info-mcp installed: $(which steam-info-mcp)"
|
|||||||
pip install mcp-pyautogui
|
pip install mcp-pyautogui
|
||||||
echo "✓ mcp-pyautogui installed: $(which mcp-pyautogui)"
|
echo "✓ mcp-pyautogui installed: $(which mcp-pyautogui)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== USDC/x402 Finance Plugin (#965) ==="
|
||||||
|
if ! command -v node &> /dev/null; then
|
||||||
|
echo "⚠ Node.js not found. Installing via Homebrew..."
|
||||||
|
if ! command -v brew &> /dev/null; then
|
||||||
|
echo "ERROR: Homebrew not found. Install Node.js manually: https://nodejs.org"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
brew install node
|
||||||
|
fi
|
||||||
|
echo "✓ Node.js $(node --version) installed"
|
||||||
|
|
||||||
|
# Install hermes-finance-plugin globally so npx can run it
|
||||||
|
echo "Installing hermes-finance-plugin..."
|
||||||
|
npm install -g hermes-finance-plugin
|
||||||
|
echo "✓ hermes-finance-plugin installed: $(which hermes-finance-plugin 2>/dev/null || echo 'npx available')"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Verify Finance Environment Variables ==="
|
||||||
|
if [ -z "${FINANCE_PRIVATE_KEY:-}" ]; then
|
||||||
|
echo "⚠ FINANCE_PRIVATE_KEY not set."
|
||||||
|
echo " Generate a burner wallet: https://vanity-eth.tk"
|
||||||
|
echo " Then: export FINANCE_PRIVATE_KEY=***"
|
||||||
|
echo " Add to ~/.zshrc or ~/.bashrc for persistence."
|
||||||
|
else
|
||||||
|
echo "✓ FINANCE_PRIVATE_KEY is set"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${FINANCE_RPC_URL:-}" ]; then
|
||||||
|
echo "⚠ FINANCE_RPC_URL not set (required for mainnet)."
|
||||||
|
echo " Sepolia testnet: https://sepolia.infura.io/v3/KEY"
|
||||||
|
echo " Or use a public RPC: https://rpc.sepolia.org"
|
||||||
|
else
|
||||||
|
echo "✓ FINANCE_RPC_URL is set"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${FINANCE_NETWORK:-}" ]; then
|
||||||
|
echo "⚠ FINANCE_NETWORK not set (default: sepolia)."
|
||||||
|
echo " Options: sepolia, base-sepolia, base, ethereum"
|
||||||
|
else
|
||||||
|
echo "✓ FINANCE_NETWORK=${FINANCE_NETWORK}"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Verify Steam API Key ==="
|
echo "=== Verify Steam API Key ==="
|
||||||
if [ -z "${STEAM_API_KEY:-}" ]; then
|
if [ -z "${STEAM_API_KEY:-}" ]; then
|
||||||
echo "⚠ STEAM_API_KEY not set."
|
echo "⚠ STEAM_API_KEY not set."
|
||||||
echo " Get one at: https://steamcommunity.com/dev/apikey"
|
echo " Get one at: https://steamcommunity.com/dev/apikey"
|
||||||
echo " Then: export STEAM_API_KEY=your-key-here"
|
echo " Then: export STEAM_API_KEY=***"
|
||||||
echo " Add to ~/.zshrc or ~/.bashrc for persistence."
|
echo " Add to ~/.zshrc or ~/.bashrc for persistence."
|
||||||
else
|
else
|
||||||
echo "✓ STEAM_API_KEY is set"
|
echo "✓ STEAM_API_KEY is set"
|
||||||
@@ -35,10 +78,13 @@ echo "Add Terminal (or whatever runs the heartbeat loop)."
|
|||||||
echo ""
|
echo ""
|
||||||
echo "=== Quick Smoke Test ==="
|
echo "=== Quick Smoke Test ==="
|
||||||
echo "Test steam-info-mcp:"
|
echo "Test steam-info-mcp:"
|
||||||
echo " echo '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}' | steam-info-mcp"
|
echo " echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | steam-info-mcp"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Test mcp-pyautogui:"
|
echo "Test mcp-pyautogui:"
|
||||||
echo " echo '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}' | mcp-pyautogui"
|
echo " echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | mcp-pyautogui"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Both should return JSON with available tools."
|
echo "Test hermes-finance-plugin (dry-run):"
|
||||||
|
echo " echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | npx -y hermes-finance-plugin"
|
||||||
|
echo ""
|
||||||
|
echo "All should return JSON with available tools."
|
||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
|
|||||||
Reference in New Issue
Block a user