Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Description from the maker (original English)
Resolve Farcaster casts to deep-link URLs for any supported Farcaster client app. No auth.
These three are never merged into one score: opinions, measurements and popularity are different things.
We have not written a plain-language summary for this one yet. Below are the facts we read from the registry.
Pick the AI app you use, then copy the settings.
You need
Which app do you use?
Pick your app above. We remember it, so next time the button says “Install in …”.
Works in Claude on the web, desktop and mobile:
https://forkcaster.xyz/mcpRun this in your terminal
claude mcp add --transport http forkcaster https://forkcaster.xyz/mcpPaste into .cursor/mcp.json in your project, or ~/.cursor/mcp.json
{
"mcpServers": {
"forkcaster": {
"url": "https://forkcaster.xyz/mcp"
}
}
}Your browser asks to open the app, then the app shows the settings and asks you to confirm. Key values are left empty for you to fill in.
Paste into .vscode/mcp.json in your project
{
"servers": {
"forkcaster": {
"type": "http",
"url": "https://forkcaster.xyz/mcp"
}
}
}Your browser asks to open the app, then the app shows the settings and asks you to confirm. Key values are left empty for you to fill in.
ChatGPT connects only to servers on the internet:
https://forkcaster.xyz/mcpMenu names can differ slightly by plan and over time.
Your choice stays on this device in a cookie; no account needed.
These settings are generated from the maker’s registry entry. If the README says otherwise, follow the README.
Tool names and descriptions exactly as the server reported them on Nov 7, 2026. The maker wrote them, not us; we show them as plain text.
get_cast_linkResolve a Farcaster cast — identified by username/fid + short hash, or by its full 40-hex hash — and return the URL that opens it in a target Farcaster client app (or all supported apps). Also returns the author's signed-contract status; URLs are returned regardless of signed sta
We check information anyone can see; we have not read the code. Checks that do not apply are left out of the count. · Checked Nov 7, 2026
The registry name was verified against the domain forkcaster.xyz (the registry checks domain ownership)
We have not read this repo from GitHub yet
We have not read this repo from GitHub yet
Responded in 131 ms (HTTP 200)
No sign-in needed
Read 1 tool descriptions and found no hidden instructions to the AI
Compared with every active server in this category in the registry. Stars count only when the publisher owns the repo.
It passed 3 of 3 applicable basic checks. We have not read the code. Before adding a key or token, see what access the server asks for and use a key with only the permissions you need.
The registry does not list a required key, though some servers ask you to sign in on first use.
It runs in the maker’s cloud. Connect over the internet with nothing to install, but what you send passes through the maker’s servers.
The GitHub repo last changed –, and the latest version (1.0.0) reached the registry 16 days ago.
Apps that support MCP, such as Claude Desktop, Claude Code, Cursor and VS Code. It connects by URL, so apps that accept remote MCP servers work too, such as ChatGPT in developer mode.
In the same category: Spec Driven Development, Remote Desktop Commander, Foliosio
Servers in the same category from other makers, sorted by stars.
Reading a card:★ GitHub starsiThree pillars: reviews · checks · usageiUpdated = latest version published to the registryi
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
Read your support inbox, tag conversations and save draft replies from your AI agent. Never sends.
Copied from the maker’s repo as written. PopNx MCP has not reviewed or endorsed it. HTML, images and scripts are removed. Links lead off this site.
Audience: coding agents. Prefer facts over prose. Update this file when architecture or commands change.
Micro-site that mirrors Farcaster cast URLs for sharing without the Farcaster app.
/{username}/{castHash} where castHash matches 0x[0-9a-fA-F]+ — short or full form/zoo/0x895ecc74 and /decent-artlu/0xbb54368f → SSR cast card + linksworker/cast-source.ts + worker/shim.ts on the shared worker/race.ts kernel) — see "Cast source race" below. shim (https://shim.artlu.xyz/wc/{username}/{hash}, owner-run Snapchain reader with a keep-warm cron) anchors completeness and hosts the ban-signal read (/signed/{fid} — fleet-raced like every shim read); farcaster.tv is fast but quote-blind; raw nodes haatz/snap.farcaster.xyz:3381 are embed-complete but text-blind (the gateway strips the mention placeholder characters from text — shim owns the @username substitution — so mention-bearing raw wins are held until canonical text settles or is proven unavailable) and get the fid by racing the shim fleet's /f/{username} against their own /v1/userNameProofByName (first numeric answer wins, loser aborted; the shim-side winner is labeled with its fleet member id), plus a full hash requirement (castById 400s on short hashes). All upstream calls are capped at 5s.AbortSignal.timeout) — shim's cold sharded lookups can otherwise hang 15s+; timeout ⇒ unreachable. Strict statuses are the JSON variant's job (see below)GET https://shim.artlu.xyz/signed/{fid} → {"signed":true|false}. Unsigned authors: registry clients flagged requiresSigned (e.g. farcaster) are suppressed from the action row; the check fails open — unknown state suppresses nothing{castId} embeds) and unofficial — it races as the partial tier only (700ms HTML box, client-side quote fill-in). react-farcaster-embed's own default endpoint is farcaster.tv — always pass castData so it never fetches. The live stats row (replies/recasts/likes) is still fetched client-side from farcaster.tv — it sends access-control-allow-origin: * and answers for unsigned authors too; any failure just leaves the row absentThe system is a waypoint layer over Farcaster: every public URL shape maps to exactly one canonical surface, and every page answers one question — *which app opens this?* There is no database, no KV, and no edge cache on this side — data is raced across a fleet of sources on every request. The product is the routing verdict, not stored content.
Cloudflare Worker (Hono; dual-domain; registration-order routing)
├─ routes: cast · profile · casts-page · aliases · mcp · /transparency · /api · /t ├─ RESOLUTION KERNEL — worker/race.ts │ staggerDelays · collectTerminals · collectiveRace │ ├─ raceShimProbe — every shim-shaped read (profiles, quotes, signed, │ │ aliases, MCP): cloud + the potato fleet │ └─ raceCast — provider roster (shim ∥ potatos ∥ haatz ∥ snap ∥ │ farcaster.tv) + completeness box + race telemetry ├─ SOURCES: shim (cloud) · potato fleet (replicas) · haatz/snap (raw │ nodes) · farcaster.tv (partial, quote-blind) ├─ RENDERERS: resolve once, render twice — SSR HTML + strict JSON twin ├─ STATEFUL LAYER: fc_client cookie → last-used badge, auto-open, pill │ order (client-side writes; the server only reads) └─ TELEMETRY: one anonymous Analytics Engine datapoint per resolve → offline snapshots → /transparency
Verdicts are collective — no single host is the source of truth. Fleet legs start staggered (tiny random steps behind the leader; a fast winner cancels the stragglers before they fire). The first affirmative answer wins; a consulted miss (asked, no data) is a vote — not-found settles only when every miss-authoritative leg has consulted and missed; errors surface only when every leg errored. Any one host — including cloud shim — can die without changing a single verdict. The price is deliberate: a true miss resolves as fast as the slowest leg, not the fastest.
Consumer-matched strictness. HTML fails open — a human always gets a page (found → card, partial → self-healing card, unreachable → link-only). JSON is the strict record (404 / 503 + Retry-After, availability-gated fields, quotes never dropped). Chat link-preview bots get HTML + OG meta unconditionally: negotiation is ?format=json / Accept, never UA sniffing, and SSR output is byte-identical for every device.
Design laws (enforced everywhere; detail in the sections below):
worker/track.ts and declared on /transparency. Pages make no third-party requests — logos are self-hosted because a hotlinked logo would leak every visitor's IP to that client's servers. The one cookie carries a client slug, nothing else.SHELL_SCRIPT reads the local fc_client cookie and applies the last-used badge, the resolution-first primary ("Open in X" — a direct anchor with the pill's exact wiring — beside a muted "More apps" sheet trigger), and the auto-open state machine after parse (the sheet's auto-open toggle syncs from the same cookie). That is what makes the full page — cookie-holders included — servable from the 12h edge shell; profiles cache 10 minutes (they are mutable) under the same client-side model. Bump SHELL_SALT in worker/edge-cache.ts to invalidate every shell (there is no global purge; v3 dropped the pre-sheet UI — bare pill row + on-page auto-open chip; v4 reached cached shells with the Safari-safe sheet CSS; v5 reached them with the kind-aware chooser copy and single-offer bare-row shape).worker/client-registry.ts.| Layer | Choice | Notes |
|---|---|---|
| Runtime / package mgr | Bun | lockfile: bun.lock; scripts use bun run |
| UI (landing) | Preact 11 | SPA assets; entry src/main.tsx |
| Cast pages | Hono on Cloudflare Worker | SSR in worker/ |
| Cast data | Provider race (cast-source.ts) | shim ∥ farcaster.tv ∥ haatz ∥ snap ∥ shim-potato fleet — first validated win; completeness box for HTML, complete-leg fleet roster for JSON; see "Cast source race" below |
| URL embed cards | unfurl.artlu.workers.dev | called via the UNFURL service binding (workers.dev→workers.dev fetch is blocked, CF error 1042); GET /?u={url} → OG title/description/image (cached, fails open to hostname-only) |
| Cast embed | react-farcaster-embed | rendering only — castData always supplied, never fetches |
| Live stats | client-side script → farcaster.tv | LIVE_STATS_SCRIPT in cast-scripts.ts; fills the SSR-stripped stats slot from the browser; fails silent (row absent); the SSR data path is fleet-raced server-side |
| SSR | preact-render-to-string | after await FarcasterEmbed({ castData }) |
| Bundler | Vite 8 | @preact/preset-vite + @cloudflare/vite-plugin |
| Lint | Biome | bun run lint |
| Typecheck | bun check | tsc-compatible, built into Bun; no typescript pkg |
| Tests | bun:test | bun test (cast route + client registry) |
bun install
bun run dev # wrangler dev (local worker + assets) bun run build # vite build → dist/ bun run typecheck # bun check (tsc-compatible; reads tsconfig*.json) bun run deploy # build + wrangler deploy bun run lint # biome lint . bun test # worker tests (cast route + client registry) bun run cf-typegen # wrangler types → worker-configuration.d.ts
/
├── index.html # SPA shell → /src/main.tsx ├── vite.config.ts # preact + cloudflare + react→preact aliases ├── wrangler.jsonc # main=worker/index.ts; not_found_handling=none ├── public/farcaster-embed.css # linked from SSR HTML ├── public/logos/ # per-client marks for the action row ├── quilcast-router-shortcut.md # the iOS/macOS share-sheet router: mapping, build/sign, distribution ├── shortcuts/ # generated .shortcut artifact (unsigned; bun run shortcut:quilcast) ├── worker/ │ ├── index.ts # Hono app; export { app }; default { fetch } │ ├── client-registry.ts # client registry (verified templates; zero-dep, vendored-able) │ ├── clients.ts # fc_client cookie binding (last-used + auto-open prefs) │ ├── shim.ts # shim identity client (routes, ShimCast/ShimUser, /wc /h /f /u /user /casts-and-replies /signed, /refresh as the index warmer; fleetBases parses the fleet var) │ ├── race.ts # fleet-race kernel: RaceLeg, staggerDelays, collectTerminals, collectiveRace — shared by cast-source + shim │ ├── cast-source.ts # provider race: shim ∥ potato fleet ∥ haatz ∥ snap ∥ farcaster.tv (+ the /~/{hash} hash-only rescue); fleet stagger + collective miss parity │ ├── cast-page.tsx # resolve (race) → render orchestration: SSR / 404 / link fallback │ ├── cast-data.ts # pure Shim → embed-data mapping + embed helpers │ ├── cast-actions.tsx # client sheet — chooser shapes (single-offer bare row / trigger + dialog), pills byte-identical inside │ ├── cast-scripts.ts # inline client scripts (TRACK/CLIENT/DEEPLINK/AUTO/LIVE_STATS/QUOTE_FILL) │ ├── profile-page.tsx # profile landings: resolve-once/render-twice, waypoint law │ ├── warpcast.ts # Warpcast channel API client (zod contract; /v1/channel — keyless, first-party) │ ├── channel-page.tsx # channel landings: resolve-once/render-twice, waypoint law │ ├── shell.tsx # shared page frame (head/meta/CSS/footer) — cast pages + profiles + /transparency │ ├── cast-ref.ts # MCP cast-ref: zod contract + shim-direct resolution │ ├── mcp.ts # MCP SDK v2 server factory (get_cast_link) — only SDK import site │ ├── cast-route.test.ts # bun:test; mock.module + exact-URL fetch mock │ ├── clients.test.ts # registry unit tests: templates, gating, signed rule, deeplinks │ ├── cast-source.test.ts # race unit tests: eligibility, box, miss/error, abort, adapter mapping │ ├── race-stats-store.ts # lmdis snapshot read-through TTL cache (10min TTL / stale 24h / 2s cap) │ ├── fid-race-store.ts # mirror of the above for the "Author id races" snapshot (key fid-race) │ ├── vendor/lmdis-sdk.ts # vendored lmdis SDK (Upstash-shaped seam; get/set only) │ └── shims/server-only.ts # empty stub for Next.js marker package ├── scripts/ │ ├── race-stats.ts # offline stats job: AE SQL → snapshot → lmdis (dotenvx env; --dry) │ ├── race-stats-lib.ts # pure query builders (fixture-
Only the beginning is shown; read the rest in the repo