get_profile
Get profile
Who Sigao Li is: identity, career narrative, contact links, and self-introduction.
lang
com.sigaoliPublished by sigaoli.comOfficial publisherv1.0.0Updated Oct 24, 2026
Read-only access to Sigao Li's profile, CV and case studies. Bilingual (EN/ZH).
Read-only remote MCP server exposing Sigao Li's personal profile, CV and project case studies in English and Chinese.
Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.
Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.
Read https://sourceweft.com/mcp/com.sigaoli%2Fprofile/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.Add this to your client's mcpServers config.
{
"mcpServers": {
"sigao-li-personal": {
"type": "http",
"url": "https://api.sigaoli.com/mcp"
}
}
}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_inbox/ and commit. A pre-commit hook works out the country from the coordinates against Natural Earth polygons, archives the originals outside git, derives masters with a maximum edge of 4096px and the EXIF stripped, asks a vision model for the bilingual caption, and registers everything in photos.json — the map, the counts and the assistant's knowledge all follow from there/privacyAny 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.
docs/bilingual-sync-design.md.src/data/cv.json or cv.zh.json — the hook keeps the other in step entry by
entry, so changing one role doesn't touch the rest. The timeline, /resume.json and
/llms-full.txt all render from it. Replace public/files/pdf/CV__Sigao_Li.pdf alongside.
(skills is deliberately left out of the sync: the two languages use different shapes there
and the renderer handles both.)src/lib/i18n.ts._inbox/ and commit. The pre-commit
hook files them by GPS, archives the originals to _originals/ (never committed), derives
serving masters with a maximum edge of 4096px, writes bilingual captions with a vision model, and registers them in
src/data/photos.json. Photos without GPS go in _inbox/<country-id>/ instead. Counts,
the map and the AI knowledge pack all follow from photos.json automatically.
See docs/photo-ingest-design.md.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.Two chains run from a pre-commit hook: photo ingest, and bilingual sync. After cloning, point git at the tracked hooks directory once:
Without this neither runs — photos dropped into _inbox/ stay there, and the two
languages stop tracking each other. Both exit immediately when there is nothing to
do, so an ordinary commit is unaffected.
Both need .env (copy .env.example): the model calls for captions, translation
and the staleness check all go through that gateway.
Regenerating the lock file. package-lock.json has to be built on Linux. npm
records only the optional dependencies it can resolve on the machine it runs on,
so a lock made on Windows omits what sharp needs on a runner and npm ci refuses
to install. After changing dependencies here, run the Relock on Linux workflow
from the Actions tab; it rebuilds the lock, proves npm ci works, and commits the
result. Ordinary pushes never need it — and if you forget, the verify job says so.
Push to master → deploy.yml builds and deploys to Pages. Pushes to v2 build
without deploying (verification).
Two more workflows watch rather than gate, each emailing on failure and neither able to hold up a release:
The audit used to gate the build. It reads the day's advisory database rather than this repository, so an untouched commit could go from green to red overnight — which is exactly what stopped a photo batch from shipping on 13 September. Knowing is worth an email; being blocked is not.
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.
Source: README.md at commit 6268656
Get profile
Who Sigao Li is: identity, career narrative, contact links, and self-introduction.
langList experience
Full CV: current role, professional experience, research, education, volunteering, awards, skills, certifications.
langGet case study
Full text of one project case study by slug (challenge / approach / impact). Call with an unknown slug to get the list of valid slugs.
langslugrequired90% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
Declare a license in the repository or the registry entry.
src/├── pages/ # en routes + zh/ mirrors; llms.txt / resume.json / knowledge.json endpoints├── components/ # Nav, Hero, WorldMap, Lightbox, CommandK, ChatWidget …│ └── pages/ # shared page bodies rendered by both locales├── content/ # cases & research (en) + cases-zh & research-zh (generated, reviewed)├── data/ # cv.json / cv.zh.json / photos.json (GPS + bilingual alts)│ └── knowledge/ # persona sources for the AI assistant (about / faq / guidelines / boundaries)├── lib/ # i18n dict, GSAP lifecycle helper, site config│ └── knowledge/ # knowledge-pack pipeline (same-source layers + build-time privacy guard)└── assets/ # photo originals (optimized at build; originals never shipped)worker/ # Cloudflare Worker: /chat (SSE) + /classify (intent) + /mcp (MCP server)└── src/core/ # runtime-agnostic logic; Cloudflare specifics live only in src/adapter/public/zoe/ # Zoe's clip library (600p VP9 alpha, lazy-loaded; idle loads first)docs/ # zoe-production-handbook.md — clip production specs & prompt cardsgit config core.hooksPath .githooks