Skip to main content
Glama
andrei-vintila

avemguvern

avemguvern.ro

A one-question site: does Romania currently have a (full, non-interim) government? Current answer: Nu! — Inca e interimar Bolojan! Designated PM: Siegfried Muresan (PNL), waiting on the investiture vote.

Everything runs in a single Cloudflare Worker (free tier):

  • Static page — minimalist verdict, served from public/, plus a nominee card whenever someone is designated but not yet voted in.

  • Public APIGET /api/status (read), POST /api/status (admin, token-protected).

  • Crowd-sourced jokesPOST /api/joke (public, body {"combo":["AUR","UDMR"],"joke":"..."}) lets visitors submit a joke for a party combination; stored but never shown live. Review them with GET /api/jokes (admin token): curl .../api/jokes -H "Authorization: Bearer $ADMIN_TOKEN".

  • MCP server — read-only get_government_status tool at /mcp (Streamable HTTP).

State is one Cloudflare KV key (current). If KV is empty the Worker serves DEFAULT_STATUS from src/index.ts, so it works before seeding.

Project layout

public/index.html   # the page (no build step)
src/index.ts        # Worker: routes /api/status, /mcp, else static assets
wrangler.jsonc      # Worker + assets + KV config
seed.json           # initial KV value

Related MCP server: Full-MCP-QA

Local development

npm install
npx wrangler dev

Then:

# read
curl http://localhost:8787/api/status

# MCP: list tools
curl -X POST http://localhost:8787/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

# MCP: call the tool
curl -X POST http://localhost:8787/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_government_status"}}'

For a local admin token during wrangler dev, create .dev.vars:

ADMIN_TOKEN=some-local-token

Deploy

npm install
npx wrangler login

# 1. Create the KV namespace, then paste the returned id into wrangler.jsonc
npx wrangler kv namespace create GOV_STATUS

# 2. Create the D1 database for joke suggestions, paste its database_id into
#    wrangler.jsonc, then apply the schema to the remote database
npx wrangler d1 create avemguvern-suggestions
npx wrangler d1 execute avemguvern-suggestions --remote --file=./schema.sql

# 3. Set the admin token (used to authorize POST /api/status and GET /api/jokes)
npx wrangler secret put ADMIN_TOKEN

# 4. Deploy
npx wrangler deploy

# 5. (optional) Seed the KV value — default already matches
npm run seed

For local development, apply the schema to the local D1 once:

npx wrangler d1 execute avemguvern-suggestions --local --file=./schema.sql

Custom domain

After the first deploy, attach avemguvern.ro in the Cloudflare dashboard (Workers & Pages → avemguvern-ro → Settings → Domains & Routes), or add to wrangler.jsonc:

"routes": [{ "pattern": "avemguvern.ro", "custom_domain": true }]

(DNS for the domain must be on Cloudflare.)

Updating the status

When the political situation changes, patch the status (only send fields that change):

curl -X POST https://avemguvern.ro/api/status \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"hasGovernment": true, "answer": "Da!", "subtitle": "Avem guvern plin!", "interim": false, "primeMinister": "..."}'

updatedAt is stamped automatically.

The nominee

nominee / nomineeParty describe the designated prime minister, separately from primeMinister (whoever actually runs the government right now, interim or not). While nominee is set, the page shows a card under the verdict, the MCP tool mentions the nomination, and the coalition builder tells you whether the coalition you picked would actually carry them.

# a new nomination
curl -X POST https://avemguvern.ro/api/status \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"nominee": "Siegfried Muresan", "nomineeParty": "PNL"}'

# voted in — the nominee becomes the PM and the card disappears
curl -X POST https://avemguvern.ro/api/status \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"hasGovernment": true, "interim": false, "answer": "Da!",
       "subtitle": "Avem guvern plin!", "primeMinister": "Siegfried Muresan",
       "nominee": "", "nomineeParty": ""}'

nomineeParty must be one of the party ids used by the page (PSD, AUR, PNL, USR, SOS, UDMR, POT, Minoritati) or ""; anything else is ignored. If the nominee matches a party leader the page already ships a cutout for (public/leaders/), that photo is reused in the card — otherwise it falls back to a silhouette.

Using the MCP server with Claude

Visit https://avemguvern.ro/mcp in a browser for setup instructions, or add it directly:

claude mcp add --transport http avemguvern https://avemguvern.ro/mcp

It exposes one read-only tool, get_government_status, that returns the current answer plus the raw status JSON.

Abuse protection

Built into the Worker:

  • Edge cachingGET /api/status is cached at the Cloudflare edge via the Cache API (caches.default) for CACHE_TTL_SECONDS (default 60), so a read flood hits KV at most ~once/minute per data center. A successful POST purges the cache so updates show immediately. The Cache API is per-colo, so a write purges only the data center that served it; other regions refresh when their TTL expires (hence the modest default — raise it in wrangler.jsonc vars for more offload if you can tolerate longer cross-region staleness after a change).

  • Per-IP rate limiting — native Workers rate-limit bindings: reads 120/min, writes 10/min (the write limit also throttles token guessing). Over-limit → 429.

  • Hardened writesPOST only accepts the known fields (hasGovernment, interim, answer, subtitle, primeMinister, nominee, nomineeParty) with correct types, clamps strings to 200 chars, rejects bodies over 2 KB (413), and compares the admin token in constant time. nomineeParty is an enum — only a known party id or "" is stored. A leaked token can't store arbitrary or huge data.

Recommended at the Cloudflare edge (dashboard):

  • WAF Rate Limiting rule (e.g. per-IP threshold on /api/* and /mcp) — this is the layer that drops abusive traffic before it counts against your quota.

  • Bot Fight Mode under Security → Bots.

Note: a zone-level Cache Rule cannot bypass the Worker here — for a Worker-owned route the Worker always executes before the cache is checked (docs). The in-Worker Cache API above is the effective edge-cache mechanism; it saves KV operations but each request still counts as one Worker invocation. To cut invocations themselves, use the WAF rate-limit rule.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables read-only interaction with Proxmox homelab VMs and containers, allowing LLM agents to list VMs, monitor status and performance metrics, view snapshots, and check cluster health through natural language queries.
    8
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides read-only access to Trading 212 accounts, enabling AI assistants to retrieve portfolio positions, account summaries, instrument data, and transaction history.
    10 npm
    5
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Read-only MCP server for Romanian TV guide, streaming catalog, and entertainment concierge, exposing 13 tools for program search, recommendations, and event detection.
    MIT