# HyperMove > Make any web3 dApp agent-callable and monetizable per call. One MCP endpoint lets AI agents discover, connect to, and pay for on-chain actions across 27+ chains. Open-source, MIT. > MCP endpoint: https://hypermove.xyz/api/mcp · Skill catalog: https://hypermove.xyz/api/skills · Manifest: https://hypermove.xyz/.well-known/webmcp.json ## Docs - [Introduction](https://hypermove.xyz/docs) - [Quickstart — 5 min](https://hypermove.xyz/docs/quickstart) - [MCP Gateway](https://hypermove.xyz/docs/mcp-gateway) - [Dream Cycle — offline memory](https://hypermove.xyz/docs/dream-cycle) - [LLM Service — model backend](https://hypermove.xyz/docs/llm-service) - [n-payment SDK](https://hypermove.xyz/docs/n-payment) --- # Introduction > **HyperMove makes any web3 dApp agent-callable — and monetizable per call.** > One MCP endpoint lets AI agents discover, connect to, and pay for on-chain > actions across 27+ chains. Open-source, MIT, built on [n-payment](/docs/n-payment). _Agents: read all of these docs as one plain-text document at [`/llms.txt`](/llms.txt)._ ## What it is AI agents can reason, but they can't safely *act* on web3 without a unified, paid, guard-railed connection. HyperMove is that connection. It exposes three surfaces: - **MCP Gateway** (`https://hypermove.xyz/api/mcp`) — one endpoint where an agent searches a unified cross-chain catalog, reads live data (EVM · Stellar · XRPL), and pays per query. See [MCP Gateway](/docs/mcp-gateway). - **Tools & skills** — harness-wrapped agent-skills you install into any MCP client in one paste; each runs inside observability + policy + output-enforcement. Browse them at [/tools](/tools). - **WebMCP client** — 3 lines of HTML turn any dApp's forms and buttons into agent-callable tools, monetized with an x402-USDC paywall. ## How it works 1. An agent connects to the gateway and gets a bearer key (wallet signature or email). 2. It discovers capabilities (`search`, `data.call`, `skill.*`) over MCP. 3. When it calls a paid tool, HyperMove returns an HTTP 402 challenge; the agent settles in USDC via [n-payment](/docs/n-payment) and the call completes. 4. Every call runs inside the harness — errors captured, policy enforced, output verified. ## How to use it Pick the path that matches your goal: | I want to… | Start here | |---|---| | **Let my agent call web3** | [Connect your agent](/mcp-connect) → then the [MCP Gateway](/docs/mcp-gateway) guide | | **Give my agent memory across sessions** | [Dream Cycle](/docs/dream-cycle) — log episodes, consolidate offline, query cheap | | **Get a key with no wallet/browser** | [Get a key — no wallet needed](/docs/agent-auth) — terminal-only, for headless agents | | **Install a ready-made skill** | Browse [/tools](/tools), copy one install prompt, paste into your agent | | **Monetize my own dApp or API** | [Quickstart — 5 min](/docs/quickstart) + the [n-payment SDK](/docs/n-payment) | | **Self-host or configure the model backend** | [LLM Service](/docs/llm-service) — the standalone process behind `/scan` and Dream Cycle extraction | ## Next - [Quickstart — 5 min](/docs/quickstart) — a running paywall + an agent that pays $0.01. - [MCP Gateway](/docs/mcp-gateway) — connect, search, pull news, pay per query. - [Dream Cycle](/docs/dream-cycle) — give your agent memory across sessions, near-zero cost. - [LLM Service](/docs/llm-service) — the standalone model backend behind `/scan` and Dream Cycle extraction. - [n-payment SDK](/docs/n-payment) — `fetchWithPayment()`, `createPaywall()`, 27 chains · 14 protocols. --- # Quickstart — 5 min > Goal: a running paywall and an agent that pays $0.01 USDC to call it. > Built on [n-payment@0.29.1](https://www.npmjs.com/package/n-payment) — single peer dep on `viem`. Open-source · MIT. ## Step 1 — Install (20 sec) ```bash mkdir my-paid-agent && cd my-paid-agent npm init -y npm install n-payment viem hono ``` ## Step 2 — Provider: paywall (90 sec) ```ts // server.ts import { createPaywall } from 'n-payment/middleware'; import { Hono } from 'hono'; const app = new Hono(); app.use('*', createPaywall({ payTo: process.env.PAY_TO as `0x${string}`, chain: 'goat-mainnet', asset: 'USDC', routes: { '/api/forecast': { priceMicroUsdc: 10_000n }, // $0.01 }, })); app.get('/api/forecast', (c) => c.json({ city: 'Hanoi', temp: 31 })); export default app; ``` Verify the 402 contract: ```bash curl -i http://localhost:3000/api/forecast # → HTTP/1.1 402 Payment Required # → WWW-Authenticate: x402-USDC realm="…", price="10000", chain="goat-mainnet" ``` ## Step 3 — Consumer: pay (60 sec) ```ts // agent.ts import { createPaymentClient } from 'n-payment'; const client = createPaymentClient({ chains: ['goat-mainnet'], ows: { wallet: 'my-agent' }, policy: { maxPerTransaction: 100_000n }, }); const res = await client.fetchWithPayment('http://localhost:3000/api/forecast'); console.log(res.status, await res.json()); // → 200 { city: 'Hanoi', temp: 31 } ``` The SDK handles the 402 → sign → retry round-trip in one call. Keys never leave the OWS vault. ## Step 4 — Paid MCP server (60 sec) ```ts // paid-mcp.ts import { createPaidMcpServer, paidTool } from 'n-payment/agent'; const server = createPaidMcpServer({ name: 'MyAgentService', payTo: '0xYourAddress', chain: 'goat-mainnet', tools: [ paidTool({ name: 'forecast', description: 'Get weather forecast', price: 10_000n, // $0.01 USDC handler: async ({ city }) => ({ city, temp: 22 }), }), ], }); await server.listen(3000); ``` ## You're done - ✅ Real `HTTP 402` paywall via `n-payment/middleware` - ✅ `fetchWithPayment()` that pays $0.01 USDC - ✅ Vault-bound signing — your agent never touches a private key - ✅ 27 chains · 14 protocols — see [n-payment docs](/docs/n-payment) --- # MCP Gateway v1.0 > One endpoint — `https://hypermove.xyz/api/mcp` — makes cross-chain web3 agent-callable. > Search a unified catalog, pull daily news + AI insight, and pay per query on the > network of your choice. WorkOS sign-in, 5 free queries / 24h, then metered > `$0.001–$0.10` via x402/MPP. Modeled on Stellar Raven's proven pattern. The whole gateway lives behind `FEATURE_HYPERMOVE_MCP_GATEWAY_V1`. When off, `/api/mcp` serves the legacy 2-tool surface unchanged (60-second rollback). ## Tools (v1.0) | Tool | Tier | Purpose | |---|---|---| | `search` | T1 `$0.001` | Hybrid lexical + vector search over the cross-chain catalog | | `codemode.vector.search` | T3 `$0.10` | Pure semantic search | | `codemode.spec` / `catalog` / `describe` | T1 | Discovery: super-spec, full manifest, detail-on-demand | | `codemode.payments.networks` | T1 | List supported networks × rails × assets before paying | | `news.search` | T1 | Search daily web3 news across tracked projects | | `codemode.news.digest` | T2 `$0.01` | Per-project daily rollup | | `codemode.news.insight` | T3 `$0.10` | AI-synthesized insight for one project | > `execute` (sandboxed code) and real-time `stream.subscribe` are deferred to v1.1. ## Agent-skills install & run locally — MCP is optional HyperMove agent-skills are **self-contained**: fetch a skill's SKILL.md from `GET /api/skills/?format=md`, save it into your host's skills directory, and it autoloads and runs **in your own workspace by following its procedure — no MCP connection required**. The gateway above is only for the *external-protocol* layer: connect it when a skill needs live cross-chain data (`data.call`) or a payment (`payments.settle`). Browse the catalog at `GET /api/skills`. ## 1 — Connect your wallet (required by default) `/api/mcp` requires a key — there is no anonymous free tier. **[hypermove.xyz/mcp-connect](/mcp-connect)** connects your wallet, has you sign one free message (no gas), and issues a bearer token from that signature. Email-only sign-in via WorkOS is also available from the same page for users without a wallet. No wallet and no browser at all? See [Get a key — no wallet needed](/docs/agent-auth) for a terminal-only flow. ```bash # Wallet flow happens in-browser at /mcp-connect. Scripted equivalent (WorkOS): open https://hypermove.xyz/api/mcp/authorize # → WorkOS AuthKit → /api/mcp/callback → redirects to /mcp-connect?token=… # (send `Accept: application/json` to get { "token": "…" } back instead of the redirect) ``` Both `FEATURE_HYPERMOVE_MCP_GATEWAY_V1=true` and `FEATURE_MCP_AUTH_WORKOS=true` must be set — the master flag alone routes to the legacy 2-tool surface, which never checks auth. Local dev needs no WorkOS: set `HYPERMOVE_DEV_UNAUTHENTICATED=true` and call from `localhost`. ## 2 — Search (free tier: 5 / 24h) ```bash curl -s https://hypermove.xyz/api/mcp \ -H 'authorization: Bearer ' \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call", "params":{"name":"search","arguments":{"query":"token balances base","limit":5}}}' ``` Every response follows the **envelope contract** — read payloads via `.data`: ```jsonc { "ok": true, "data": { "hits": [ /* … */ ], "total": 5, "nextSteps": "…" } } // on failure: { "ok": false, "error": { "service": "moralis", "kind": "soft-empty", "message": "…", "hint": "…" } } ``` `kind: "soft-empty"` means *the service returned nothing* — NOT an error. Never treat it as absence. ## 3 — Daily news + insight ```bash # search today's news -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"news.search","arguments":{"query":"restaking"}}}' # AI insight for one project -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"codemode.news.insight","arguments":{"project":"x402"}}}' ``` ## 4 — Choose a network, then pay Discover options, then select chain + rail + asset via headers: ```bash # what can I pay on? -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"codemode.payments.networks","arguments":{}}}' # after the free tier, the gateway returns JSON-RPC error -32402 with an x-payment-required # challenge listing chains + assets + amounts. Select and pay: curl -s https://hypermove.xyz/api/mcp \ -H 'authorization: Bearer ' \ -H 'x-payment-chain: xrpl-mainnet' \ -H 'x-payment-rail: x402' \ -H 'x-payment-asset: RLUSD' \ -H 'x-payment: ' \ -d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"search","arguments":{"query":"nft"}}}' ``` One settlement opens a **session bundling 100 queries** at that tier — per-query gas stays viable. ## Rollback runbook (under 60s) 1. Set `FEATURE_HYPERMOVE_MCP_GATEWAY_V1=false` (or flip any single sub-flag) in the host env. 2. Redeploy / restart. `/api/mcp` immediately returns to the legacy 2-tool surface. 3. No data is dropped — flags never touch tables or sessions. Sub-flags for partial rollback: `FEATURE_MCP_AUTH_WORKOS`, `FEATURE_MCP_RATE_LIMIT`, `FEATURE_MCP_PAYWALL`, `FEATURE_MCP_DATA_ADAPTERS_V1`, `FEATURE_MCP_VECTOR_SEARCH`, `FEATURE_MCP_NEWS_V1`. ## Health & spec (public, no auth) ```bash curl https://hypermove.xyz/.well-known/webmcp.json # manifest: tools, endpoints, auth mode curl https://hypermove.xyz/api/mcp/health # { ok, gateway_enabled, tools[] } curl https://hypermove.xyz/api/mcp/spec # OpenAPI-3.1-style super spec ``` --- # Dream Cycle > **Your agent forgets everything between sessions. Dream Cycle fixes that.** > Log what happened during the day (`submit_episode_log`), trigger a consolidation pass > (`start_dream`), and the next session reads back small, durable lessons > (`query_dream`) instead of re-learning the same mistake. Zero setup — it's on by > default on the same `https://hypermove.xyz/api/mcp` endpoint you already use. ## Why this exists Every MCP session an agent runs starts from zero. If it discovers "the gripper needs a 200ms cooldown before retry" today, that lesson is gone tomorrow — unless you paste the whole transcript back into context, which is slow and expensive at scale. Dream Cycle is an offline consolidation pass, modeled on how research like ["sleep-time compute"](https://arxiv.org/abs/2504.13171) and offline memory-consolidation work: cheap, rule-based passes do most of the work; a small LLM call only touches *compressed summaries*, never raw logs. The output is a handful of short, confidence-scored memories your agent can pull in a single call — cents per day, not dollars. ## The 30-second mental model ``` during the day: submit_episode_log(agent_id, episodes) → cheap, no LLM call end of day: start_dream(agent_id, {budget_usd: 0.10}) → runs the whole pipeline next session: query_dream(agent_id, "gripper timeout") → "retry after 200ms cooldown" ``` That's the whole surface. Everything else (resources, prompts, presets, stats) is optional sugar on top of those three calls. ## For humans — what you get and what it costs - **No new infra.** Same endpoint, same auth, same bearer token. Dream Cycle just adds 5 tools to what your agent already sees when it calls `tools/list`. In this session's changes: `submit_episode_log`, `start_dream`, `get_dream_config`, `query_dream`, `get_dream_stats`. Also 4 resources and 3 prompts — see below. - **All 5 tools are free.** They never hit the paywall or the 5-free-queries/24h limit. Spend is capped by your own `budget_usd` per cycle (default $0.10), not by HyperMove's tiers. - **Nothing changes if you never call `start_dream`.** Logging with `submit_episode_log` costs nothing and does nothing until you trigger a cycle — no background jobs, no surprise charges. - **Your data stays yours.** Memories are scoped per `agent_id`, bound to whichever session first used that ID — a different account can never read or write into it. ## For agents — the fastest path to using this 1. Connect to `https://hypermove.xyz/api/mcp` the same way you already do (see [MCP Gateway](/docs/mcp-gateway) if you haven't yet — bearer token from [/mcp-connect](/mcp-connect) or the [no-wallet flow](/docs/agent-auth)). 2. Call `tools/list` — if Dream Cycle is enabled (default: yes) you'll see the 5 tools below. 3. Log episodes as you go. Trigger a cycle whenever you want. Query before you plan. No separate signup, no separate API key, no SDK to install — it's 3 JSON-RPC calls against a tool surface you already discover automatically. ## Pick your agent_id once, then it's yours The **first** call to `submit_episode_log` or `start_dream` for a new `agent_id` claims it for your session. After that, only your session can write to it — pick a stable, unique string (e.g. your robot's serial number, your bot's slug) and reuse it every day. ## 1 — Log what happened ```bash curl -s https://hypermove.xyz/api/mcp \ -H 'authorization: Bearer ' \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{ "name":"submit_episode_log", "arguments":{ "agent_id":"robot-42", "episodes":[{ "episode_id":"run-2026-07-26-001", "agent_id":"robot-42", "timestamp":"2026-07-26T09:00:00Z", "task_type":"pick_and_place", "outcome":"failure", "steps":[ {"action":"grip"}, {"action":"retry"}, {"action":"grip_again","error":"gripper timeout"} ], "tags":["pick_and_place","gripper"] }] } }}' ``` Returns `{ "ingested_count": 1, "rejected": [] }`. Call this as many times a day as you want — resubmitting the same `episode_id` is a safe no-op, never a duplicate. No LLM call `outcome` accepts exactly three values: `success`, `failure`, or `timeout` — there is no `"partial"` state. For a task that partly succeeded, pick whichever of the three best matches the primary goal (usually `success` if the main objective was met despite a caveat). This is also declared as an `enum` on the tool's `inputSchema`, so MCP clients that render tool schemas surface the valid values without a round-trip. happens here; this step is free and near-instant. ## 2 — Trigger a Dream Cycle ```bash curl -s https://hypermove.xyz/api/mcp \ -H 'authorization: Bearer ' \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{ "name":"start_dream", "arguments":{"agent_id":"robot-42","config":{"budget_usd":0.10,"preset":"balanced"}} }}' ``` Returns `{ "run_id":"…", "status":"started" }` and, once the pipeline finishes inside that same call, the run reaches `completed` or `partial` (if the budget ran out — still useful, never wasted). Presets: `frugal` (cheapest, fewer clusters), `balanced` (default), `thorough` (most coverage, higher cost). Call this once a day, or whenever you have a batch of new episodes worth consolidating — there's no scheduler, you decide when. ## 3 — Query before you plan ```bash curl -s https://hypermove.xyz/api/mcp \ -H 'authorization: Bearer ' \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{ "name":"query_dream", "arguments":{"agent_id":"robot-42","query":"gripper timeout","top_k":5,"min_confidence":0.5} }}' ``` Returns memories sorted by relevance: ```jsonc { "memories": [ { "memory_id":"…", "type":"error_pattern", "content":"gripper timeout after retry", "confidence":0.9, "importance":0.8 } ] } ``` Read this **before** your agent starts planning a task — it's the whole point. ## Skip the query — read memories as MCP resources instead If your MCP client supports resources, you can pull the same data with no arguments to fill in — just the right URI for your `agent_id`: | Resource | Contents | |---|---| | `hypermove:///agents/{agent_id}/dream/summary` | Last run's status + how many memories exist | | `hypermove:///agents/{agent_id}/dream/rules` | High-confidence rules | | `hypermove:///agents/{agent_id}/dream/errors` | Known error patterns | | `hypermove:///agents/{agent_id}/dream/stats` | Budget spent, stages completed, memory count | ## Skip the prompt engineering — use the built-in prompts | Prompt | What it does | |---|---| | `dream/summarize_today(agent_id)` | Plain-language summary of the last cycle | | `dream/suggest_policy_updates(agent_id, task_type)` | Concrete policy changes for one task type | | `dream/compare_before_after(eval_scores_json)` | Compares your own before/after eval numbers | These are templates, not tools — an MCP client that supports `prompts/get` renders them pre-filled; there's nothing extra to call. ## Check in on a cycle ```bash -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"get_dream_stats","arguments":{"agent_id":"robot-42"}}}' -d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"get_dream_config","arguments":{"agent_id":"robot-42"}}}' ``` `get_dream_stats` → `last_run_at`, `status`, `budget_used_usd`, `stages_completed`, `memories_count`, and (additive) `stage_summaries` — a coarse, non-content-leaking breakdown of what each stage did: how many episodes preprocessing discarded and why, how many clusters extraction attempted/skipped/failed, and how many of extraction's candidate insights pruning actually promoted vs. discarded. If `memories_count` stays `0` after a run with real episode data, check `stage_summaries.pruning_summary` — a nonzero `candidates_extracted` with `candidates_promoted: 0` means extraction genuinely found something that didn't survive pruning (not a silent failure to process your data). `get_dream_config` → whatever `budget_usd`/`preset` you last set. Before filing a "this looks broken" report, check `GET /api/mcp/health` (the gateway process) and `GET https://hypermove.duckdns.org/llm/health` (the extraction service) — both return a `commit` field (added 2026-07-27) so you can confirm the fix you're expecting is actually deployed, rather than guessing from a maintainer's say-so. ## What a fresh agent needs for its first promoted memory **One episode with at least one non-empty `steps` entry is enough.** There is no meaningful confidence/importance threshold standing between a fresh agent and its first promoted memory — pruning's defaults (`min_confidence: 0`, `min_importance: 0`) accept anything, and every new memory is inserted at `confidence: 0.5, importance: 0.5`, already well above that floor. A cluster of size 1 (a single unmatched episode) is a valid cluster; there's no minimum cluster size either. What actually determines whether you get a memory: - **`steps: []` (zero steps) is discarded before extraction ever sees it** — this is the one real filter, and it shows up in `stage_summaries.preprocessing.discard_reasons` as `empty_steps`. Give every episode at least one `steps` entry with a real `action`. - **Extraction has to find something "rule/preference/error_pattern/fact"-shaped.** A single bland episode (e.g. `{"action": "did the thing"}` with no error, no context) may legitimately produce zero insights for its cluster — that's a correct outcome for low-signal input, not a bug. `stage_summaries.extraction.candidates_extracted` tells you whether this happened; `clusters_failed`/`clusters_skipped` tell you whether it was a transient extraction failure or a budget cutoff instead. - **`stage_summaries` is the tool for "why didn't anything promote," not guesswork.** Read `pruning_summary.candidates_extracted` vs. `candidates_promoted` first, every time, before assuming something is broken. If a query on a fresh agent still returns nothing after a run with genuinely descriptive episode content (real `action`s, real `error`s on failures), check `stage_summaries` before filing a report — it will show you exactly which stage the signal was lost at. ## What's on by default, and how to turn it off Dream Cycle ships **on**, same as the rest of the MCP Gateway's v3.0+ tools — no flag to flip to start using it. If you need to disable it (e.g. to roll back a bad deploy): ```bash FEATURE_MCP_DREAM_CYCLE=false ``` One env var removes all 5 tools, all 4 resources, and all 3 prompts in one flip — nothing else on the gateway changes. ## Known limits (Phase 1 — honest, not hidden) - **`start_dream` still always runs immediately when called** — the manual path is unchanged and always available, on-demand, regardless of the scheduler below. - **Server-side `trigger_criteria` enforcement is opt-in, not default (2026-07-27).** By default, nothing runs your cycles for you — set `trigger_criteria` in `config` and it's saved either way, but only *enforced* automatically if the operator has enabled `FEATURE_MCP_DREAM_SCHEDULER=true` (off by default; see "Server-side scheduling" below for why). Without it, exactly as before: you decide when to call `start_dream`. - **No quality benchmark is published yet.** The pipeline is fully functional and budget-safe; the "how much better did my agent get" number isn't measured in this release. - **Best for motion/episode-style agents.** Dream Cycle assumes step-by-step episode logs with an outcome (success/failure/timeout) — it's not a general-purpose memory store for arbitrary unstructured text. ## Server-side scheduling (opt-in, added 2026-07-27) If you set `trigger_criteria` in `start_dream`'s `config` (`time_window_utc`, `min_episodes`, `min_raw_tokens`), it's always persisted — but by default nothing reads it back automatically. An operator running this MCP gateway can opt into server-side enforcement: ```bash FEATURE_MCP_DREAM_SCHEDULER=true ``` Once enabled, an in-process hourly tick checks every agent's `trigger_criteria` and fires `start_dream` on your behalf when your conditions are met — no external cron, no script you have to remember to run. This is deliberately **opt-in, not the default** (unlike every other Dream Cycle flag) because it's the first feature in this gateway that autonomously spends budget across every registered agent with no per-call human trigger. Two independent ceilings bound worst-case cost per tick regardless of how many agents are configured: `DREAM_SCHEDULER_MAX_BUDGET_USD_PER_TICK` (default $1.00 total across all agents per tick) and `DREAM_SCHEDULER_MAX_AGENTS_PER_TICK` (default 20) — an agent beyond either ceiling is deferred to the next tick, not silently dropped. `get_dream_stats`'s `triggered_by` field (`'manual'` or `'scheduler'`) tells you which path produced the last run. If you were already running your own external scheduler (e.g. a cron job calling `start_dream` directly), nothing about that changes — the two paths are independent and can coexist; enabling the server-side scheduler on top of an existing external one will just mean both fire (consider disabling one if you do). Full technical detail, data model, and pipeline internals: [`docs/prd/dream-cycle-v1.md`](https://github.com/phamdat721101/hypermove-app/blob/main/docs/prd/dream-cycle-v1.md) in the repo. --- # LLM Service > **A separate, standalone process (`services/llm/server.ts`) that keeps model > API keys off the main app entirely.** It powers two things: turning a URL > into an agent-callable MCP server (`POST /scan`, used by [/tools/generate](/tools/generate)) > and Dream Cycle's memory extraction (`POST /dream/extract`, called automatically > by `start_dream` — you never call this route yourself). Most people reading this > page will never need to run their own copy of it. ## Live endpoint (agents: start here) HyperMove's own deployment of this service is already running, reachable over HTTPS (Caddy-fronted, Let's Encrypt cert) at: ``` https://hypermove.duckdns.org/llm ``` No setup, no key, no signup — every example below works against this address right now. Confirm it's up before anything else: ```bash curl https://hypermove.duckdns.org/llm/health # { "ok": true, "provider": "bedrock", "commit": "f1202e41...", "deployed_at": "2026-07-27T09:02:00Z" } ``` `commit`/`deployed_at` (added 2026-07-27) tell you which build is actually running — a build-time constant injected by `scripts/deploy-vps.sh`, not a runtime `git` lookup. Check this before filing "is X actually fixed yet" — compare `commit` against the commit SHA you expect to be live rather than trusting a maintainer's unverifiable "should be deployed now." The service itself still listens on plain HTTP internally (`localhost:3001` on the host); Caddy terminates TLS and reverse-proxies `/llm/*` → `:3001` (stripping the `/llm` prefix), so every route documented below is reachable as `https://hypermove.duckdns.org/llm/`. The bare `http://:3001` address is no longer the documented public entry point — prefer the HTTPS domain above for anything crossing the public internet. If you're building an agent that uses **Dream Cycle** (`submit_episode_log` / `start_dream` / `query_dream` over MCP), you don't need this address at all — `start_dream` already calls this service internally, server-side. This address only matters if you're calling `/scan` directly, or self-hosting your own copy — see [Dream Cycle](/docs/dream-cycle) if that's not you. ## Do I need this page? Almost certainly not as a Dream Cycle or MCP Gateway **user** — extraction is already hosted and configured for you (see [Dream Cycle](/docs/dream-cycle)). This page is for: - **Agents/integrators** calling `POST /scan` directly to turn a URL into an MCP server (the endpoint above is all you need). - **Operators** deploying HyperMove's own infrastructure who need to configure or redeploy this service. - **Self-hosters** who want to point Dream Cycle's extraction stage at their own model, provider, or key instead of HyperMove's default. - **Anyone curious** what's actually running behind `/tools/generate` and Dream Cycle's `start_dream`. ## What it is A plain Node HTTP server (no framework), run with `tsx`, deployed as its own process independent of the main Next.js app. One config surface (`LLM_PROVIDER` + a matching API key) serves every route below — there's no per-route credential duplication. ```bash cd services/llm npm install PORT=3001 BEDROCK_API_KEY=... npx tsx server.ts # or: npm start (reads services/llm/.env.local automatically) ``` ## Configuration — one provider, one key ```bash LLM_PROVIDER=bedrock # bedrock (default) | anthropic | openai # bedrock — uses the Converse API (uniform request/response shape across # every Bedrock model, so swapping BEDROCK_MODEL never needs a code change): BEDROCK_API_KEY=... BEDROCK_REGION=us-east-1 # optional, this is the default BEDROCK_MODEL=deepseek.v3.2 # optional, this is the default — the # cheapest DeepSeek variant Bedrock hosts # ($0.62/$1.85 per 1M input/output tokens, # vs DeepSeek-R1's $1.35/$5.40) # anthropic: ANTHROPIC_API_KEY=... ANTHROPIC_MODEL=claude-sonnet-4-20250514 # optional # openai: OPENAI_API_KEY=... OPENAI_MODEL=gpt-4o # optional # Optional — quota/payment tracking (Supabase Postgres). Omitted → every # quota check no-ops permissively (unlimited, dev-friendly default). DATABASE_URL=postgres://... ``` Switching provider — or switching model within Bedrock — is one env var; every route (`/scan`, `/dream/extract`) picks it up automatically, no code change. **Dream Cycle users never need any of the above.** This key lives entirely on this service; the 5 Dream Cycle MCP tools (`submit_episode_log`, `start_dream`, `get_dream_config`, `query_dream`, `get_dream_stats`) never accept or require an API key parameter — see [Dream Cycle](/docs/dream-cycle). ## Routes | Route | Purpose | |---|---| | `GET /health` | `{ ok: true, provider: "bedrock" }` — confirms the process is up and which provider it's configured for. | | `POST /scan` | Crawl a URL, ask the model to synthesize an MCP manifest (tools + schemas) from the page, return it plus a ready-to-paste `mcpConfig`. Powers [/tools/generate](/tools/generate). | | `POST /dream/extract` | Extract `{rules, preferences, error_patterns, facts}` from a short episode-cluster summary. Called automatically by Dream Cycle's `start_dream` — see [Dream Cycle](/docs/dream-cycle). Not meant to be called directly by end users. | | `GET /quota?wallet=0x…` | Read a wallet's free-scan quota + tier. | | `POST /quota/consume` | Decrement one free scan for a wallet (`402` when exhausted). | | `POST /upgrade` | Verify an on-chain payment (native BTC or ERC-20) and upgrade a wallet to the pro tier for 30 days. | | `POST /register` | Persist a generated MCP manifest so it's servable later at `//`. | | `GET` / `POST //` | Serve a previously registered MCP manifest (GET) or handle JSON-RPC calls against its tools (POST) — the hosted-MCP surface `/scan` + `/register` produce. | ## 1 — Turn a URL into an MCP server ```bash curl -s -X POST https://hypermove.duckdns.org/llm/scan \ -H 'content-type: application/json' \ -d '{"url":"https://example.com","wallet":"0xYourWalletAddress"}' ``` Returns: ```jsonc { "manifest": { "name": "example-com-mcp", "tools": [ /* … */ ] }, "mcpConfig": { "mcpServers": { "example-com-mcp": { "url": "https://your-deploy/0x…/example-com-mcp", "transport": "http" } } }, "crawlData": { "url": "...", "title": "...", "toolCount": 2 } } ``` Paste `mcpConfig` straight into any MCP client's config file — the generated server is already registered (via `/register`, called internally) and servable at that URL. ## 2 — Dream Cycle extraction (you don't call this directly) ```bash curl -s -X POST https://hypermove.duckdns.org/llm/dream/extract \ -H 'content-type: application/json' \ -d '{"summary":"agent failed: gripper timeout during pick_and_place, retried without cooldown","max_output_tokens":150}' ``` Returns: ```jsonc { "rules": ["Add cooldown period after gripper timeout before retrying pick_and_place"], "preferences": [], "error_patterns": ["Gripper times out during pick_and_place when retried immediately without cooldown"], "facts": [] } ``` This exists so `src/lib/mcp/dream/extract.ts` has a real model to call — `start_dream` invokes it once per cluster automatically. On any error or malformed model output it degrades to `{rules:[],preferences:[],error_patterns:[],facts:[]}` rather than crashing the pipeline, so a Dream Cycle run always reaches a terminal status even if this service is briefly unreachable. ## Point Dream Cycle at your own instance (advanced, optional) By default, the main app's `DREAM_EXTRACT_URL` (falls back to `LLM_SERVICE_URL`, then `http://localhost:3001/dream/extract`) points at HyperMove's own deployed instance over the internal loopback (same host, server-to-server — no public network hop, so plain HTTP is correct there) — nothing to configure. If you're self-hosting and want a different model or provider for extraction specifically: ```bash # In the main app's env (not this service's): DREAM_EXTRACT_URL=https://your-llm-service.example.com/dream/extract ``` Your instance just needs to implement the same two-field-in, four-array-out contract shown in step 2 above. ## Deploy This service is deployed independently of the main app (its own process, its own restart lifecycle) — typically via `pm2`: ```bash cd services/llm npm install pm2 start "npx tsx server.ts" --name llm-service --cwd $(pwd) pm2 save ``` Health-check after any redeploy: ```bash curl http://localhost:3001/health # { "ok": true, "provider": "bedrock" } ``` Front it with a reverse proxy for public HTTPS access (HyperMove's own deployment uses Caddy — see the live endpoint at the top of this page): ``` hypermove.duckdns.org { handle_path /llm/* { reverse_proxy localhost:3001 } reverse_proxy localhost:3003 # the main Next.js app } ``` `caddy reload --config --adapter caddyfile` applies changes with zero downtime (no restart needed) once Caddy is already running against that file. ## A routing gotcha if you add a new route here `server.ts` uses a hand-rolled router (plain `if` chains on `req.method` + `url`, no framework) with one greedy catch-all near the bottom: any **exactly-two-segment** path (`//`) is claimed by the hosted-MCP handler before anything defined after it in the file gets a chance to run. If you add a new route whose path also happens to have exactly two segments (like `/dream/extract` did), place it **above** that catch-all in the file — same as `/quota/consume` and `/dream/extract` are today — or it will silently return `204` for every request instead of running your handler. --- # n-payment > **n-payment@0.29.1** on npm — collapses x402 + ERC-8004 + BTC lending + OWS signing into one call. > Single peer dependency: `viem`. Dual CJS/ESM output. MIT-licensed. ## Three principles 1. **Agents never touch a private key.** OWS vault holds keys and enforces per-tx policy. 2. **BTC is the treasury; USDC is borrowed just-in-time.** Lock PegBTC → borrow → pay → repay → unlock. 3. **Five lines, not five hundred.** `fetchWithPayment()` unifies the lifecycle. ## Consumer — fetchWithPayment() ```ts // agent.ts import { createPaymentClient } from 'n-payment'; const client = createPaymentClient({ chains: ['goat-mainnet', 'base-mainnet'], ows: { wallet: 'my-agent' }, policy: { maxPerTransaction: 100_000n, // $0.10 cap per call maxPerDay: 10_000_000n, // $10.00 daily cap treasury: 'BTC', // PegBTC-collateralized rateLimit: { maxRequests: 100, windowMs: 60_000 }, }, }); const res = await client.fetchWithPayment('https://hypermove.xyz/api/paid-endpoint'); console.log(await res.json()); // 200 OK, paid in ~1.2s ``` Under the hood: 1. GET the target URL → server returns HTTP 402 + WWW-Authenticate: x402-USDC. 2. Vault checks policy (spend cap, chain allowlist, token allowlist). 3. If treasury: 'BTC', the lending adapter locks PegBTC + borrows the exact USDC. 4. EIP-3009 authorize signature is produced inside the OWS vault. 5. The client retries with X-Payment header → server returns 200. ## Provider — createPaywall() ```ts // server.ts import { createPaywall } from 'n-payment/middleware'; import { Hono } from 'hono'; const app = new Hono(); app.use('*', createPaywall({ payTo: '0xYourAddress', chain: 'goat-mainnet', asset: 'USDC', routes: { '/api/paid-endpoint': { priceMicroUsdc: 10_000n }, // $0.01/call }, })); app.get('/api/paid-endpoint', (c) => c.json({ ok: true })); export default app; ``` One middleware call. Works with Express, Hono, Fastify, or raw fetch. ## Paid MCP Server ```ts // paid-mcp-server.ts import { createPaidMcpServer, paidTool } from 'n-payment/agent'; const server = createPaidMcpServer({ name: 'MyAgentService', payTo: '0xYourAddress', chain: 'goat-mainnet', tools: [ paidTool({ name: 'forecast', description: 'Get weather forecast', price: 10_000n, handler: async ({ city }) => ({ city, temp: 22 }), }), ], }); await server.listen(3000); ``` ## 14-protocol matrix | Protocol | Coverage | Chains | |---|---|---| | x402 | HTTP 402 handshake | GOAT, Base, Arb, Optimism | | EIP-3009 | Authorized USDC transfer | Base, Arb, Polygon | | MPP | Multi-Party split | GOAT, Base | | Stellar Channels | Sub-cent micropayments | Stellar | | Wormhole NTT | Native Token Transfers | Base, Solana, Sui | | Circle Gateway | USDC mint/burn | Base, Solana | | RLUSD | Ripple USD stablecoin | XRPL, Base | | XRPL x402 | XRPL-native MPT paywall | XRPL | | ERC-8004 | Agent identity + reputation | Base, Celo | | Aave V3 GHO | Idle USDC treasury yield | Base, Arb | | OWS Vault | Vault-bound signing | all 27 chains | | PegBTC Lending | JIT USDC from BTC | GOAT | | Flare FXRP | Bridged XRP gas + asset | Flare | | Initia VIP | Validator-incentive settle | Initia | ## Links - [npm · n-payment](https://www.npmjs.com/package/n-payment) - [GitHub](https://github.com/phamdat721101) - [Book a demo](https://calendly.com/phamdat721101/30min) ---