An interactive art installation for tech conferences — draw a bird, let Snowflake Cortex AI animate it, and watch it fly across the big screen!
- A booth volunteer types
birdsin a terminal (an alias for./start-workshop.sh, installed bysetup-laptop.sh) - The attendee types
$bird-workshopto activate the workshop skill - The skill guides them through five phases:
- Draw a bird in a browser canvas that auto-opens on the booth laptop
- Describe how it should fly
- Snowflake Cortex generates a Bird Passport + CSS flight animation via
CORTEX.COMPLETE()(mistral-large2) - The bird gets INSERT'd into a Snowflake table and appears on the live projected display
Throughout the flow, the agent narrates what's happening — so attendees walk away understanding Cortex Code, MCP tool use, Snowflake Cortex, and Snowflake itself.
- Snowflake account with Cortex AI enabled (specifically
mistral-large2) - Cortex Code CLI (
cortex) installed - Snowflake CLI (
snow) installed —brew install snowflake-cliorpipx install snowflake-cli - Node.js 18+ (for the MCP server / canvas)
setup-laptop.sh writes the Snowflake connection config for you — you don't need to run snow connection add manually.
snow sql -f setup.sql # as ACCOUNTADMIN — creates warehouse, DB, FLOCK table, DATA_BIRDS_USER, registers public keygit clone https://github.com/YOUR_ORG/SnowBirds && cd SnowBirds
./setup-laptop.shsetup-laptop.sh does five things:
- Adds a
databirdsconnection to~/.snowflake/config.toml(keypair auth pointing atDATA_BIRDS_USER) - Creates
.envfrom.env.example - Installs MCP server dependencies and registers the
birdsMCP server withcortex - Pre-approves the workshop's MCP tools in
~/.snowflake/cortex/permissions.jsonso attendees never see permission prompts forstart_canvas,push_to_flock, etc. - Adds shell aliases:
birdsfor attendee sessions andbigscreenfor the projected display
Open a new terminal (so the alias loads) and you're done.
birdsWhen cortex opens, the attendee types $bird-workshop to activate the skill. The browser canvas auto-opens during Phase 2 — no URL clicking needed.
For the projected display, run this in a separate terminal:
bigscreenThat serves index.html locally and opens the live display in the browser. It continuously syncs manifest.json from Snowflake, so a single master display laptop can show birds submitted from all attendee laptops.
That's it. rsa_key.p8 is distributed out-of-band (1Password / encrypted Slack / USB at the booth) and dropped into the repo root before running setup-laptop.sh — it is not committed. See "Rotating the booth keypair" below.
setup-laptop.sh— one-time per laptop. Snowflake connection, MCP registration, pre-approvals, shell aliases.start-workshop.sh— what thebirdsalias runs. Launchescortex --allowed-tools ...with a scoped allowlist: the 5 workshop MCP tools,Read, and safe Bash patterns (ls,cat,grep,find,head,tail). Anything outside the list triggers a normal approval prompt — which is your signal the agent drifted off the skill's intended path.start-display.sh— what thebigscreenalias runs. Serves the live projected display fromindex.html.setup-mcp.sh— legacy. MCP-only registration. Usesetup-laptop.shinstead.
Every booth laptop authenticates as the same DATA_BIRDS_USER via the same RSA private key. No password (no rotation drift, no password-policy rejections), no browser flow (no per-attendee OAuth popups), no MFA prompts. Combined with the --allowed-tools allowlist and the pre-approved MCP cache, attendees should see zero permission prompts during a normal workshop run.
rsa_key.p8 / rsa_key.pub are git-ignored — they must never be committed. The private key is distributed out-of-band to each booth laptop. After each event:
# 1. Generate a fresh keypair (writes into the repo root; both files are git-ignored)
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
# 2. Copy the new public key body (between BEGIN/END PUBLIC KEY, no newlines)
# into setup.sql's ALTER USER ... SET RSA_PUBLIC_KEY = '...'
# 3. Push the new key to Snowflake (zero-downtime — Snowflake supports two active
# public keys per user via RSA_PUBLIC_KEY + RSA_PUBLIC_KEY_2)
snow sql -f setup.sql
# 4. Commit ONLY setup.sql — never rsa_key.p8 or rsa_key.pub.
git add setup.sql && git commit -m "Rotate booth keypair"
# 5. Re-distribute the new rsa_key.p8 to each booth laptop via 1Password / encrypted
# Slack DM / USB. Do NOT email, do NOT commit, do NOT paste into a public channel.To fully invalidate a compromised key immediately:
snow sql -c <admin-conn> -q "ALTER USER DATA_BIRDS_USER UNSET RSA_PUBLIC_KEY; ALTER USER DATA_BIRDS_USER UNSET RSA_PUBLIC_KEY_2;"Then generate + install a new keypair as above.
SnowBirds/
├── .cortex/ ← Cortex Code CLI project config
│ ├── skills/
│ │ └── bird-workshop/
│ │ └── SKILL.md ← 5-phase workshop instructions
│ └── mcp-server/
│ ├── server.js ← MCP server (canvas, Cortex AI, Snowflake)
│ ├── canvas-server.js ← Local drawing canvas web server
│ ├── canvas.html ← Browser-based drawing interface
│ ├── bird-processor.js ← Background removal (sharp)
│ └── snowflake-client.js ← Snow CLI wrapper for CORTEX.COMPLETE() + FLOCK ops
├── setup-laptop.sh ← Per-laptop booth setup (one-time)
├── start-workshop.sh ← Per-attendee launcher (what the `cortex` alias runs)
├── setup-mcp.sh ← (legacy) MCP-only registration
├── setup.sql ← Snowflake environment setup (one-time per account)
├── rsa_key.p8 ← Booth private key (git-ignored; distributed out-of-band)
├── rsa_key.pub ← Booth public key (git-ignored; canonical copy lives in setup.sql)
├── index.html ← Live projected display (reads manifest.json)
├── manifest.json ← Display cache, synced from Snowflake by bigscreen
├── sync-display-manifest.py ← Polls Snowflake FLOCK table for the display
├── birds/ ← Submitted bird images
├── .env.example ← Environment variable template
└── .mcp.json ← MCP server registration for the cortex CLI
FLOCKtable — Every bird is a row with AI-generated fields (species, personality, flight animation)FLOCK_STATSdynamic table — Real-time aggregation of flock statisticsHOURLY_SUBMISSIONSdynamic table — Hourly aggregates for forecasting / anomaly detectionCORTEX.COMPLETE()— Callsmistral-large2to generate bird data + CSS animation- Keypair auth —
DATA_BIRDS_USERauthenticates via RSA private key; no passwords, no MFA, no OAuth flow
Forked from MLH/BirdsBirdBirds (gemini-cli-skill branch by Jon).
Adapted for Snowflake Cortex by the team.
made with ♥ and ❄️