Skip to main content
Glama

PPN Hub Free MCP

CI

Keyless, read-only MCP entry point for PPN Hub — try 75 public APIs (weather, elevation, Japanese addresses, environment, science, civic data) from any MCP client with zero signup.

https://mcp-free.ppn-hub.com/mcp        (Streamable HTTP)

What you get

Tool

Free tier behaviour

search_apis

Full anonymous discovery across the public surface (484 endpoints)

execute_api

Allowlisted GET read endpoints run without a key (see allowlist.json)

environment_brief

Works without a key; costs 10 units of the daily budget (it fans out)

Everything else — POST/write operations, higher limits, the full surface — needs a free ppn_live_* key: get one at https://ppn-hub.com/quickstart and point your client at https://mcp.ppn-hub.com/mcp (one-click OAuth supported).

Client setup (Claude Code example)

claude mcp add --transport http ppn-free https://mcp-free.ppn-hub.com/mcp

Related MCP server: @aiiq/mcp

Free tier limits

Limit

Value

Accounting

Per IP, burst

10 requests / minute

Cloudflare-native rate limiter

Per IP, daily

100 units / day (UTC)

Approximate — KV, best-effort

Shared global cap

5,000 units / day — last-resort abuse cap

Exact — Durable Object

environment_brief cost

10 units per call

—

Regular requests cost 1 unit (initialize/ping/tools/list are free). The per-IP counter lives in KV: reads and writes aren't atomic, so a racing client can squeeze a few extra units through (the per-minute limiter bounds how far). The global cap is enforced exactly by a single GlobalBudget Durable Object instance — Cloudflare serializes requests to one DO instance, so there's no equivalent race there, and it fails closed on exhaustion. When you hit a limit you get a 429 with a pointer to the quickstart. Abusive patterns (operation brute-forcing, endpoint enumeration sweeps) earn a temporary 24h block. Client IPs are stored only as salted SHA-256 hashes.

How it works (architecture)

This repo is a thin proxy Worker — Hono only, a handful of small source files, no access to any private code:

MCP client ──POST /mcp──▶ ppn-hub-free-mcp (this repo)
                            │  1. ban list / per-minute limiter / daily
                            │     budgets — per-IP (KV, approximate) +
                            │     global (Durable Object, exact, fail-closed)
                            │  2. JSON-RPC inspection: batches and
                            │     off-allow-list methods rejected outright,
                            │     GET-only allowlist, deny-by-default
                            │  3. inject a dedicated free-tier backend key
                            ▼
                          mcp.ppn-hub.com/mcp (main gateway, private repo)
  • src/index.ts — routing, limit enforcement, upstream forwarding

  • src/rpc.ts — JSON-RPC inspection (protocol-level rejection + per-tool policy) + tools/list description rewriting

  • src/guard.ts — per-IP KV budget, global Durable Object budget client, abuse detection, ban list

  • src/global-budget.ts — the GlobalBudget Durable Object itself (the exact half of the daily budget accounting)

  • src/allowlist.ts + allowlist.json — the GET-only allowlist (deny-by-default)

  • scripts/gen-allowlist.mjs — regenerates allowlist.json from the LIVE public surface; endpoints whose backing code bills per call (AI providers) are excluded there, and it refuses to write a possibly-truncated list

The backend key held by this Worker is a normal free-tier consumer key — it is not a service/admin credential. It is only ever attached to allowlisted execute_api calls and environment_brief; every other method is forwarded anonymously or rejected before it reaches the upstream gateway, which enforces its own auth and quotas independently.

Develop / deploy

pnpm install
pnpm test               # vitest: guards, allowlist, JSON-RPC inspection, DO budget
pnpm run coverage        # same, with a coverage report
pnpm run typecheck
pnpm run lint
cp .dev.vars.example .dev.vars   # once; wrangler.toml binds FREE_KV even locally
pnpm run dev             # wrangler dev

wrangler.toml binds FREE_KV at the top level too, so it's present even under wrangler dev — and this Worker fails closed (503 FREE_TIER_MISCONFIGURED) whenever FREE_KV is bound but IP_HASH_SALT isn't set, rather than falling back to a fixed salt. .dev.vars.example has a dummy IP_HASH_SALT (any string works locally) and an empty FREE_TIER_BACKEND_KEY (execute_api/environment_brief calls return 503 FREE_TIER_UNAVAILABLE until you set one; search_apis/tools/list/etc. work without it). Copy it to .dev.vars (gitignored) before running dev.

CI (.github/workflows/ci.yml) runs install → typecheck → lint → test on Node 22.x and 24.x. Deploys are manual:

export CLOUDFLARE_ACCOUNT_ID=...                 # wrangler reads this; no account_id in wrangler.toml
pnpm exec wrangler kv namespace create FREE_KV   # once; paste the id into wrangler.toml
pnpm exec wrangler secret put FREE_TIER_BACKEND_KEY --env production
pnpm exec wrangler secret put IP_HASH_SALT --env production   # openssl rand -hex 32
pnpm run deploy                                  # also applies the GlobalBudget DO migration

License

MIT

Related MCP Connectors

Related MCP Servers