Read-only access to Sigao Li's profile, CV and case studies. Bilingual (EN/ZH).
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste into ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
Personal website of Sigao Li β AI Product Manager Β· Spatial Data Scientist. From maps to models, and the products in between.
Bilingual (English at /, δΈζ at /zh/), built with Astro + Tailwind CSS v4 + GSAP,
deployed to GitHub Pages via GitHub Actions. Launched 2026-06-11, replacing the previous
Jekyll (academicpages) site.
docs/)/llms.txt, /llms-full.txt, /resume.json (JSON Resume), /knowledge.json, /.well-known/mcp.json, JSON-LD, and a robots.txt that explicitly welcomes AI crawlers/privacy| Command | Action |
|---|---|
npm run dev | Dev server at localhost:4321 (Astro 7 runs it as a daemon β stop with npx astro dev stop) |
npm run build | Production build to dist/ |
npm run preview | Serve the production build locally |
node scripts/translate.mjs | Re-translate changed en content β zh (needs .env, see .env.example; manually edited zh files are never overwritten) |
node scripts/check-links.mjs | Internal link integrity check over dist/ |
node scripts/verify-nav.mjs η | Playwright interaction suites (run against a local server) |
npm run dev (in worker/) | Chat + MCP Worker at localhost:8787 (wrangler; secrets in worker/.dev.vars, never committed) |
node scripts/verify-chat.mjs | E2E chat-widget test (needs both dev servers running) |
node scripts/verify-zoe.mjs | E2E for Zoe's action state machine (append ?zoe-fast locally to compress minute-scale timers) |
node scripts/verify-typeroute.mjs | E2E for the intent-driven typing clip and the bilingual 404 page |
Any Playwright suite that waits on Zoe's state must pin the clock (
Date.prototype.getHours = () => 14): between 23:00 and 06:00 she starts the session asleep, sostatenever reachesidleand the run just times out.
When adding a Zoe clip, decide who prewarms it and when at the same time. A clip that is only fetched at playback stalls on a slow connection, and the stage shows nothing until it decodes. Prewarming has been missed three times already. Note
warm()takes the file name (sit-to-loaf), not theZOEkey (sitToLoaf).
The chat panel is rebuilt on every navigation β
transition:persistkeeps Zoe's stage, not the panel. Anything that lives only in panel DOM is gone the moment a visitor clicks a link. The streaming reply, the guidance chip and the unsent draft each had to be given module state plus a path back throughpaint(); the chip was lost for weeks before anyone noticed. So when adding persistent UI here, answer two questions up front: how doespaint()rebuild it, and should it ride along insessionStoragewith the history? Measure geometry only once the panel is visible βscrollHeightis 0 while it is hidden, which silently writesheight: 0px.
Turnstile guards
/chatand/classify. It must never guard/mcp. That endpoint exists so machines can read Sigao's profile β it is in the official registry β and Turnstile exists to stop machines. It also costs nothing to serve: the tools read the knowledge pack and never call a model. The static outlets (llms.txt,knowledge.json,.well-known/mcp.json) are served by Pages and never reach the Worker at all.
Locally, Turnstile uses Cloudflare's always-pass test keys β sitekey in
site.tsbehindimport.meta.env.DEV, secret inworker/.dev.vars. The real key rejects headless browsers, which is exactly its job, so every suite that drives a real Worker would fail against it. The real secret exists only in production, set withwrangler secret put. A corollary worth remembering: the production happy path cannot be verified from a script β reaching it needs a human in a real browser. Automation can still prove the gate is up (a request with no credential must return 403).
One knowledge layer, three outlets: /llms-full.txt for passive crawlers, a chat assistant
(POST /chat, SSE) for humans, and an MCP server (/mcp, Streamable HTTP, no auth β tools:
get_profile / list_experience / get_case_study) for visiting agents, both served from
api.sigaoli.com (Cloudflare Worker, code in worker/). The knowledge pack
(/knowledge.json) is assembled at build time from the
same sources as the pages β persona markdown, cv.json, case studies, photo stats β so any
content edit propagates to all three outlets on the next deploy, no manual step. A privacy
guard fails the build if sensitive patterns (phone numbers, IDs, coordinates) ever leak into
the pack.
Alongside each reply the chat runs a lightweight intent classifier (POST /classify, a small
model) to suggest the single most relevant page, and can remember a returning visitor's name β
both kept entirely in the visitor's own browser (opt-in, clearable via "Forget me"), never on a
server. Visitors in the EU/EEA/UK have their chat and classification routed to an EU-hosted
provider, never the China-direct API. What the site stores and sends is described in plain
language at /privacy.
src/content/cases/*.md (en), then run the translate
script β or edit the -zh files directly (they're override-protected afterwards).src/data/cv.json (+ cv.zh.json); the timeline, /resume.json and
/llms-full.txt all render from it. Replace public/files/pdf/CV__Sigao_Li.pdf alongside.src/lib/i18n.ts.src/assets/photos/<country>/, add entries to
src/data/photos.json (run node scripts/extract-gps.mjs for coordinates). Photo stats
in the AI knowledge pack update automatically.src/data/knowledge/*.md; the knowledge pack rebuilds on
every deploy and the assistant follows within ~10 minutes (Worker-side cache TTL).scripts/zoe-board2.mjs β zoe-qc2.mjs β zoe-prod2.mjs) keys, QCs, mirrors and
encodes them into public/zoe/. New actions = one clip + one row in the ZOE table in
ChatWidget.astro; specs and prompt cards in docs/zoe-production-handbook.md.Push to master β GitHub Actions (.github/workflows/deploy.yml) audits, builds and
deploys to Pages. Pushes to v2 build without deploying (verification).
The Worker deploys separately: cd worker && npx wrangler deploy (secrets via
wrangler secret put; custom domain api.sigaoli.com bound in the Cloudflare dashboard).
When a batch changes both, deploy the Worker first β the chat UI calls its endpoints, so a
site push ahead of the Worker leaves a brief window where those calls 404.
A deploy takes up to a minute to reach every edge location. Checking immediately reads the previous version, which has twice looked like a broken deploy when nothing was wrong β wait, then check.
Daily chat usage is at https://api.sigaoli.com/usage (last seven days, plus whether today has
hit the cap). The cap itself is DAILY_CAP in worker/src/core/quota.ts; when it trips it emails
once via Cloudflare Email Routing.
β οΈ Never click "Sync fork". This repository began as an academicpages fork; syncing would reset
masterto the upstream template. If that ever happens again:git push --force origin <good-commit>:master.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/sigao-li-personal-mcp-server)<a href="https://allmcps.com/mcp/sigao-li-personal-mcp-server"><img src="https://allmcps.com/api/badge/sigao-li-personal-mcp-server?style=directory" alt="Sigao Li β personal MCP server on AllMCPs" /></a>