Server data from the Official MCP Registry
23 agent tools via x402: crypto data, tx checks, web fetch. MCP pays Base USDC; HTTP adds 3 rails.
About
23 agent tools via x402: crypto data, tx checks, web fetch. MCP pays Base USDC; HTTP adds 3 rails.
Remote endpoints: streamable-http: https://horizonpulse.dev/mcp
Security Report
Horizon Pulse is a pay-per-call x402 API server (Next.js) with a local stdio MCP wrapper; the visible code shows SSRF guards on fetch/http/extract routes, pinned payTo addresses, and secrets sourced from environment variables rather than hardcoded values. Findings are limited to informational and low-severity notes: the server intentionally makes arbitrary outbound HTTP requests to user-supplied URLs (expected for a fetch/proxy product), the local MCP server holds a buyer wallet key that can spend funds within caps, and the provided source was truncated so the full dependency and route implementations could not be verified.
4 files analyzed · 4 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
How to Install & Connect
Available as Local & Remote
This plugin can run on your machine or connect to a hosted endpoint. during install.
Documentation
View on GitHubFrom the project's GitHub README.
Horizon Pulse
Pay-per-call crypto market pulse, signals, funding, yield rankings, portfolio risk, gas, plus public URL fetch → clean text, universal http proxy, and extract → structured fields for AI agents, monetized with the x402 protocol on Base mainnet and Solana mainnet (USDC, exact, same price on both).
This repo is a Next.js App Router service ready to deploy (e.g. Vercel) and push to:
https://github.com/horizon-pulse/horizon-pulse.git
Public host
| Role | URL |
|---|---|
| Canonical | https://horizonpulse.dev |
| Backup (Vercel) | https://horizon-pulse-seven.vercel.app |
Prefer horizonpulse.dev in agent docs, OpenAPI, and clients. The *.vercel.app URL remains a working fallback.
What it is
- Agents hit live paid HTTP endpoints (crypto routes frozen;
/api/fetch+/api/http+/api/extract+/api/x402-check+/api/bazaar-check+/api/market/search+/api/market/quote+/api/screenshot+/api/search+/api/pdfnon-crypto LIVE — see below). - Unpaid requests receive HTTP 402 with x402 v2 requirements: canonical wire is the
PAYMENT-REQUIREDheader, with oneacceptsentry per network: Base (CAIP-2eip155:8453, Base USDC, the treasury below) and Solana (CAIP-2solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp, USDC mint, the Solana payTo below). Seedocs/solana-rail.md. - Retry with an x402 v2
PAYMENT-SIGNATUREheader. The facilitator (Coinbase CDP on Base, PayAI on Solana) verifies + settles, then the route returns live data. - No stubbed prices or fake APYs: Coinbase Exchange (CoinGecko fallback) for spot; CoinGecko OHLC (Coinbase candles fallback) for signals; OKX for perpetual funding (Binance/Bybit are often geo-blocked on Vercel); DefiLlama for yield pools; public RPC
balanceOffor portfolio (real balances only);eth_feeHistory/eth_gasPricefor gas (real fees only);/api/fetchreturns best-effort cleaned text from a requested public URL (SSRF-safe, size/time capped);/api/httpis a raw universal proxy (filtered headers, text|base64 body, SSRF-safe) priced $0.01 for volume;/api/extractreturns best-effort structured page fields from a URL or HTML (SSRF-safe, size/time capped) priced $0.015. - Never invent metrics; never advertise routes that 404; never cite a cached balance —
/statusreads live USDCbalanceOfonpayTo. Honest: currentpayTois interim Coinbase-custodial (not Safe/multisig).
Catalog status (post first settlement)
| Fact | Value |
|---|---|
| Live paid routes | See the table below — the frozen crypto routes + /api/fetch + /api/http + /api/extract + /api/x402-check + /api/bazaar-check + /api/market/search + /api/market/quote + /api/screenshot + /api/search + /api/pdf |
| First settle | /api/pulse $0.005 — tx 0xedbd1a51… |
| Crypto policy | Frozen — six crypto routes: no price changes |
| Fetch | Non-crypto LIVE: clean-text ($0.02) |
| Http | Non-crypto LIVE: universal proxy ($0.01 volume price) |
| Extract | Non-crypto LIVE: structured HTML ($0.015) |
Coming-soon / placeholder / 404 routes are not listed on /, /status, or this README.
Agent how-to (x402 v2)
GETa paid route with no payment → HTTP 402.- Read
PAYMENT-REQUIRED(v2) and pick oneacceptsentry. Confirmnetwork(eip155:8453for Base orsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpfor Solana), USDC on that network, exact atomicamount, and that network'spayTo. - Sign and retry the same
GETwithPAYMENT-SIGNATURE(v2 primary; do not rely on legacyX-PAYMENTas the settle path). - On success: the facilitator settles on the network you paid on; response body is the live JSON for that route.
Optional: OPTIONS on a paid route returns discovery + the same PAYMENT-REQUIRED challenge without charging.
Agent integration
- Skill file:
/skill.md, plain markdown an agent can follow end to end (generated frompublic/openapi.jsonbylib/agent-skill.ts). - Guide:
/agents: pay flow, skill and MCP setup. - Local MCP server:
mcp/, stdio, every live route as a tool, pays the x402 challenge from a buyer wallet you configure, with per-call and per-session caps (quote-only without a key). Not published to npm; install from this repo. - Hosted MCP:
POST https://horizonpulse.dev/mcp(streamable HTTP) for MCP clients that can pay x402 themselves. Payments over MCP are USDC on Base only; the paid HTTP routes accept Base, Solana, Polygon and XRPL.
Treasury (payTo)
| Field | Value |
|---|---|
| payTo | 0x5b32c973596078a967562ca652761404f19be0e9 |
| Basename | horizonpulsebase.base.eth (resolves to the payTo above; a label only, the 402 carries the hex address) |
| Custody | Interim Coinbase-custodial Base address (owner-controlled). Not a Safe or multisig. |
| Upgrade path | Move later to a non-custodial Safe / multisig when ready — same catalog and prices. |
| USDC (Base) | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| Network | base (CAIP-2 eip155:8453) |
| Facilitator | https://api.cdp.coinbase.com/platform/v2/x402 |
| Solana payTo | BjY98A6dS3GGLZdz2zHy8wK7XAwnQgNhCc66mfmBTRPz (operator-controlled; pinned in lib/solana-config.ts; payments credit its USDC token account) |
| USDC (Solana) | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| Solana network | CAIP-2 solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (exact only) |
| Solana facilitator | PayAI https://facilitator.payai.network |
Do not use the retired address 0xe16A1b12404cB2EbC6e783beCA6E2A9253c3dC7E.
PAY_TO may be overridden via env, but defaults to the address above. Agents should treat current payTo as the live settlement destination; custody shape may change later without changing route prices.
Endpoints
Paid (frozen crypto routes + fetch + http + extract + x402-check + bazaar-check + market/search + market/quote + screenshot + search + pdf):
| Route | Price | Auth |
|---|---|---|
GET /api/pulse | $0.002 USDC (2000 atomic) | x402 v2 |
GET /api/signals | $0.005 USDC (5000 atomic) | x402 v2 |
GET /api/yield | $0.02 USDC (20000 atomic) | x402 v2 |
GET /api/portfolio?address=0x… | $0.04 USDC (40000 atomic) | x402 v2 |
GET /api/gas | $0.002 USDC (2000 atomic) | x402 v2 |
GET /api/funding | $0.003 USDC (3000 atomic) | x402 v2 |
GET /api/fetch?url=https://… | $0.02 USDC (20000 atomic) | x402 v2 |
GET / POST /api/http | $0.01 USDC (10000 atomic) | x402 v2 |
GET / POST /api/extract | $0.015 USDC (15000 atomic) | x402 v2 |
GET /api/x402-check?url=https://… | $0.01 USDC (10000 atomic) | x402 v2 |
GET /api/bazaar-check?url=… | $0.01 USDC (10000 atomic) | x402 v2 |
GET /api/market/search?q=… | $0.005 USDC (5000 atomic), untested price | x402 v2 |
GET /api/market/quote?url=https://… | $0.005 USDC (5000 atomic), untested price | x402 v2 |
GET /api/screenshot?url=https://… | $0.02 USDC (20000 atomic) | x402 v2 |
GET /api/search?q=…&n=3 | $0.03 USDC (30000 atomic) | x402 v2 |
GET /api/pdf?url=https://… | $0.02 USDC (20000 atomic) | x402 v2 |
Free (not paid APIs):
| Route | Auth |
|---|---|
GET /status | public HTML — live USDC balanceOf on payTo (honest RPC error if fetch fails) |
GET / | landing — live paid catalog + agent how-to |
GET /api/pulse
Real spot prices for BTC / ETH / SOL (Coinbase Exchange, CoinGecko fallback) with per-asset momentum, plus overall momentum / sentiment / direction (up/down/flat, a neutral description of the average 24h move; renamed from signal, see CHANGELOG.md).
runtime = 'nodejs'- Discovery: Bazaar extension +
outputSchema.input.discoverable: truein payment requirements
GET /api/signals
RSI(14), MACD(12,26,9), Bollinger bands from CoinGecko OHLC closes, plus OKX funding rates. Includes a methodology field.
runtime = 'nodejs'- Discoverable for agents
GET /api/yield
Ranked DeFi yield pools from the public DefiLlama yields API (https://yields.llama.fi/pools).
- Hard filter: TVL ≥ $10M; exclude non-finite APY and DefiLlama
outlierpools - Preference: keep stablecoin and/or single-asset (
exposure === "single") pools - Transparent ranking:
preferenceTierDESC (2 = stablecoin+single, 1 = either), then APY DESC, then TVL DESC - Every response includes a
methodologyobject (also on error bodies) - Price: $0.02 USDC (
20000atomic) runtime = 'nodejs',dynamic = 'force-dynamic'- Unpaid GET → 402 +
PAYMENT-REQUIRED(v2,eip155:8453) ·payTo0x5b32c973596078a967562ca652761404f19be0e9
GET /api/portfolio
On-chain portfolio snapshot for one EVM address (?address=0x…, required).
- Networks: Base + Ethereum mainnet (public RPCs; optional
BASE_RPC_URL/ETH_RPC_URLwith failover) - Tokens: native ETH; USDC, WETH, DAI on both chains; WBTC on Ethereum; cbBTC on Base (honest substitute — Base has no BitGo WBTC)
- USD marks via Coinbase public exchange rates (CoinGecko for the rest or on Coinbase failure); real balances only — if an RPC fails, that network is marked failed in
networks[]/warnings - Rule-based concentration score (0–100) and rule findings (largest asset weight vs 35%/50%, stablecoin share vs 15%/85%, largest chain share vs 85%, low native ETH gas balance; returned in the
suggestionsarray as plain findings, no instructions) with transparent formulas inmethodology - Price: $0.04 USDC (
40000atomic) runtime = 'nodejs',dynamic = 'force-dynamic'- Unpaid GET → 402 +
PAYMENT-REQUIRED(v2,eip155:8453) ·payTo0x5b32c973596078a967562ca652761404f19be0e9
GET /api/gas
Live gas snapshot for Base and Ethereum from public RPCs.
- Prefers
eth_feeHistory(last 20 blocks, reward percentiles 10/50/90):baseFeeGwei,priorityFeeGwei(p50),suggestedMaxFeeGwei(= 2×baseFee + priority) - Falls back to
eth_gasPricewhen feeHistory is unavailable - Transparent
timingHint: cheap / normal / expensive from percentile rank of the latest confirmed baseFee within the feeHistory window (<33 / 33–67 / >67); documented inmethodology— not a forecast - Optional ETH USD (Coinbase Exchange, CoinGecko fallback) for a simple 21k-gas transfer cost estimate (
simpleTransfer.costUsd) - Price: $0.01 USDC (
10000atomic) runtime = 'nodejs',dynamic = 'force-dynamic'- Unpaid GET → 402 +
PAYMENT-REQUIRED(v2,eip155:8453) ·payTo0x5b32c973596078a967562ca652761404f19be0e9
GET /api/funding
Live perpetual funding rates for BTC / ETH / SOL from OKX (same source as /api/signals; Vercel-friendly — not Binance/Bybit).
- Reuses
lib/okx.ts(fetchAllFunding) - Transparent
methodologyon every response (including errors) - Optional crowding hint from funding sign + magnitude only (rules: quiet / mild / elevated / extreme; side longs/shorts/neutral) — not a forecast
- Honesty: real OKX data only
- Price: $0.01 USDC (
10000atomic) — cheap poll product for agents runtime = 'nodejs',dynamic = 'force-dynamic'- Unpaid GET → 402 +
PAYMENT-REQUIRED(v2,eip155:8453) ·payTo0x5b32c973596078a967562ca652761404f19be0e9
GET /api/fetch
Fetch a public http(s) URL (?url=https://…, required) and return best-effort clean text/markdown.
- SSRF-safe: blocks localhost, private, link-local, and metadata IPs; re-checks each redirect hop
- Caps: ~200KB raw body, 8s timeout, max 3 redirects
- Preferred Content-Types:
text/html,text/plain,application/json,text/markdown - Cleaning is best-effort (strip script/style/chrome, collapse whitespace) — not a full readability engine
- Price: $0.02 USDC (
20000atomic) runtime = 'nodejs',dynamic = 'force-dynamic'- Unpaid GET → 402 +
PAYMENT-REQUIRED(v2,eip155:8453) ·payTo0x5b32c973596078a967562ca652761404f19be0e9
GET / POST /api/http
Universal agent HTTP proxy — call a public http(s) URL with optional method / headers / body; get back upstream status, filtered headers, and body (text or base64) plus contentType and elapsedMs.
- Price choice: $0.01 USDC (
10000atomic) for volume./api/fetchstays $0.02 (specialized clean-text). Prefer/api/httpfor raw proxy traffic. - Inputs:
url(required);method(GETdefault;GET|POST|HEAD|PUT|PATCH|DELETE); optional allowlistedheaders(no Cookie / hop-by-hop); optionalbodyfor POST/PUT/PATCH (size-capped) - GET query:
?url=&method=&headers=<json>; POST JSON body for full control including upstream body - SSRF-safe (shared guards with
/api/fetch): blocks private/link-local/metadata; http/https only; re-checks redirects - Caps: ~384KB response body, 64KB request body, 12s timeout, max 3 redirects
- Honesty: upstream 4xx/5xx returned in
statuswhen the hop succeeded; proxy failures use honest error codes runtime = 'nodejs',dynamic = 'force-dynamic'- Unpaid GET/POST → 402 +
PAYMENT-REQUIRED(v2,eip155:8453) ·payTo0x5b32c973596078a967562ca652761404f19be0e9 - Bazaar discovery advertises GET; POST is paid identically
GET / POST /api/extract
URL or HTML → structured page fields for agents (title, description, canonical, language, links, images, headings, json-ld, text sample).
- Price choice: $0.015 USDC (
15000atomic) — between/api/http($0.01) and/api/fetch($0.02). Same payTo + Base USDC stack. - Inputs:
url(optional ifhtmlprovided) and/orhtml(size-capped). Prefer fetchingurlwith the same SSRF-safeassertSafePublicUrlas fetch/http. - GET query:
?url=&fields=<url-encoded json>; POST JSON{ url?, html?, fields? }for html payloads - Optional
fields: up to 20 named CSS selectors, e.g.{"title":"h1","links":{"selector":"a.story","attr":"href","all":true,"limit":5}}.attristext(default),html, or any attribute (href/src resolved to absolute URLs). Unmatched fields returnnullplusfieldErrors[name](no_match,bad_selector,attr_missing,time_budget,document_too_deep,eval_failed). If none match: 422no_fields_matched, not charged. Hard 2s selector budget;:has()at most once per selector; pages nested >256 levels are refused for fields. Deterministic selectors, no AI. - Lightweight regex parse (no cheerio). Empty fields omitted. Not a full browser DOM.
- Caps: ~200KB HTML, 8s timeout, max 3 redirects (same band as
/api/fetch) - No cookie jar. Private/localhost/link-local/metadata blocked.
runtime = 'nodejs',dynamic = 'force-dynamic'- Unpaid GET/POST → 402 +
PAYMENT-REQUIRED(v2,eip155:8453) ·payTo0x5b32c973596078a967562ca652761404f19be0e9 - Bazaar discovery advertises GET; POST is paid identically
GET /status
Public page showing the live USDC balance of the payTo address on Base (via public RPC / BASE_RPC_URL balanceOf) plus the live paid catalog and agent how-to.
- On RPC failure the page shows an honest error — it does not fall back to a cached balance. Custody note: interim Coinbase-custodial
payTo(not Safe/multisig); upgrade path later. - First settle noted as
/api/pulse$0.005 (0xedbd1a51…); six crypto routes frozen;/api/fetch+/api/http+/api/extractdeliberately LIVE.
Local development
Requires Node.js 20+ (tested on 20.19).
cd horizon-pulse
cp .env.example .env.local
# fill CDP_API_KEY_ID + CDP_API_KEY_SECRET for verify/settle
npm install
npm run dev
npm run build && npm start
Environment variables (Vercel / production)
Copy from .env.example:
| Variable | Required | Notes |
|---|---|---|
X402_NETWORK | recommended | base |
PAY_TO | optional | defaults to 0x5b32c973596078a967562ca652761404f19be0e9 |
CDP_API_KEY_ID | for settle | Coinbase Developer Platform |
CDP_API_KEY_SECRET | for settle | PKCS8 PEM (store safely; never commit) |
BASE_RPC_URL | optional | overrides default Base RPC for /status + /api/portfolio + /api/gas |
ETH_RPC_URL | optional | overrides default Ethereum RPC for /api/portfolio + /api/gas |
Without CDP keys:
- Unpaid GET and OPTIONS still return correct 402 / discovery with v2
PAYMENT-REQUIRED(payTo,eip155:8453, USDC amount) — no CDP required for discovery. - Requests that include
PAYMENT-SIGNATURE(or legacyX-PAYMENT) receive 503 untilCDP_API_KEY_ID+CDP_API_KEY_SECRETare set on Vercel (settlement path). - Do not expect live GET to 500 when CDP is missing; that was a prior bug fixed by gating
withX402behind payment + CDP credentials.
Stack
- Next.js 16 (App Router) + TypeScript
@x402/next+@x402/core+@x402/evm+@x402/extensions@coinbase/x402for CDP facilitator auth headersviemfor treasury USDCbalanceOfon Base, portfolio balances, and gas feeHistory on Base + Ethereum
Deploy notes
- Create the GitHub repo / remote
https://github.com/horizon-pulse/horizon-pulse.git. - Set Vercel env vars (above). Prefer PEM secret as a single line with
\nescapes if the UI is single-line. - Deploy from
main. Ensure functions use Node.js runtime (routes already setexport const runtime = 'nodejs'). - Smoke-test:
curl -i https://horizonpulse.dev/api/pulse(or/api/yield,/api/portfolio,/api/gas,/api/funding,/api/fetch?url=https://example.com,/api/http?url=https://example.com,/api/extract?url=https://example.com) should return 402 with payment requirements pointing at the newpayTo. - Confirm
/statusshows the treasury balance for0x5b32…e0e9.
Push (from this workspace)
This scaffold does not push for you (no credentials). From a machine with GitHub auth:
cd /workspace/horizon-pulse
git init
git add .
git commit -m "feat: Horizon Pulse x402 API with new Base treasury"
git branch -M main
git remote add origin https://github.com/horizon-pulse/horizon-pulse.git
git push -u origin main
If the remote already has history:
git remote add origin https://github.com/horizon-pulse/horizon-pulse.git
git fetch origin
git pull origin main --rebase # or merge, as appropriate
git push -u origin main
Reference agent (examples/)
Unpaid 402 discovery for the live paid routes listed in the script, plus optional paid single-route when SMOKE_PRIVATE_KEY is set:
cd horizon-pulse
node examples/reference-agent.mjs
# optional paid:
SMOKE_PRIVATE_KEY=0x... SMOKE_ROUTE=/api/pulse node examples/reference-agent.mjs
See examples/README.md. Uses @x402/core + @x402/evm from this package. Never commit secrets.
Paid smoke test (/api/pulse)
End-to-end check against production (or SMOKE_BASE_URL): unpaid 402 → sign EIP-3009 / x402 with a local key → paid 200 + on-chain USDC toward payTo.
cd horizon-pulse
npm install # if needed; uses existing @x402/* + viem
SMOKE_PRIVATE_KEY=0x... node scripts/smoke-pulse.mjs
# or: npm run smoke:pulse
Optional:
SMOKE_BASE_URL=https://horizonpulse.dev
# backup: https://horizon-pulse-seven.vercel.app
BASE_RPC_URL=https://mainnet.base.org
Never paste your private key in chat, commits, or screenshots. Export it only in your local shell (e.g. Mac mini). Without SMOKE_PRIVATE_KEY, the script exits with usage help (safe dry-run).
License
Private / as designated by the horizon-pulse org.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
FinAgent
Freeby mcp-marketplace · Finance
Free stock data and market news for any MCP-compatible AI assistant.
