Today I learned

Short notes on things I worked out and didn't want to work out twice. 578 of them, kept in the open at jcdiv47/til.

Browse by topic

Recent

Adding a custom MCP server to Claude and ChatGPT

I've started exploring MCP in more detail. The web chat UIs for both Claude and ChatGPT can access MCP servers but it's not obvious how to set them up. Here's what I figured out. …

Using uvx in GitHub Actions in a cache-friendly way

I often find myself wanting to run a quick Python tool inside of GitHub Actions using uvx name-of-tool - but I don't want that to result in a network request to PyPI every time the workflow runs. I want the tool to be fetched the first time and then reused from the GitHub Actions cache for subsequent runs. …

Cloudflare CAPTCHA on at least one ampersand

I use Cloudflare's CAPTCHA (they call it a "Managed Challenge") on simonwillison.net/search/ to prevent crawlers from following every single possible combination of my faceted search UI. …

Setting a custom price for a model in AgentsView

I'm a recent convent to AgentsView, Wes McKinney's (previously of Pandas fame) Python toolkit for analyzing transcripts of coding agents from your own computer. …

Using LLM in the shebang line of a script

This comment on Hacker News inspired me to investigate patterns for using my LLM CLI tool in a shebang line: …

SQL functions in Google Sheets to fetch data from Datasette

I've been experimenting with ways to fetch data from Datasette and display it in Google Sheets. …

Running OpenClaw in Docker

I'm not brave enough to run OpenClaw (aka Clawdbot aka Moltbot) directly on my Mac, so I decided to try running it in a Docker instead container. …

Cloudflare response header transform rules

I serve Python files from my tools.simonwillison.net subdomain, which is a GitHub Pages site that's served via Cloudflare. For example: …

Previewing Claude Code for web branches with GitHub Pages

I'm a big user of Claude Code on the web, Anthropic's poorly named cloud-based version of Claude Code which can be driven via the web or their native mobile and desktop applications. …

Taking Neon I at the Crucible

I took the Neon I intensive week-long evening class at the Crucible in Oakland, with teachers Dan Kuppe and Kat. I learned to make a neon sign! It's still awaiting final infusion of gas, but I'll share photos here once it's finished. …

Downloading archived Git repositories from archive.softwareheritage.org

Last February I blogged about a neat script called sqlite-s3vfs which was released as MIT licensed open source by the UK government's Department for Business and Trade. …

Subtests in pytest 9.0.0+

pytest 9.0.0 was released on November 8th 2025. I just got around to looking at the release notes and the biggest new feature is subtests, previously available as the separate pytest-subtests plugin. …

Dependency groups and uv run

I've adopted a new (to me) pattern for my Python projects to make them easier to hack on using uv run. I'm using a PEP 735 dependency group called dev to declare my development dependencies - in particular pytest - such that running uv run pytest executes the tests for my project without me having to even think about setting up a virtual environment first. …

Using Codex CLI with gpt-oss:120b on an NVIDIA DGX Spark via Tailscale

I've written about the DGX Spark before. Here's how I got OpenAI's Codex CLI to run on my Mac against a gpt-oss:120b model running on the DGX Spark via a Tailscale network. …

Exploring OpenAI's deep research API model o4-mini-deep-research

I was reviewing some older PRs and landed this one by Manuel Solorzano adding pricing for o4-mini-deep-research and o3-deep-research to my llm-prices.com site. I realized I hadn't tried those models yet so I decided to give one of them a go. …

Testing different Python versions with uv with-editable and uv-test

A quick uv recipe I figured out today, for running the tests for a project against multiple Python versions. …

Error 153 Video player configuration error on YouTube embeds

I recently noticed that almost every YouTube video on my blog was displaying the same mysterious error message: …

Running a gpt-oss eval suite against LM Studio on a Mac

OpenAI's gpt-oss models come with an eval suite, which is described in their Verifying gpt-oss implementations cookbook. I figured out how to run it on my Mac against their gpt-oss-20b model hosted locally using LM Studio, using uv. …

Configuring GitHub Codespaces using devcontainers

GitHub Codespaces provides full development environments in your browser, and is free to use with anyone with a GitHub account. Each environment has a full Linux container and a browser-based UI using VS Code. …

Rate limiting by IP using Cloudflare's rate limiting rules

My blog was showing poor performance, with some pages taking several seconds to load or even failing entirely. …

Using Playwright MCP with Claude Code

Inspired by Armin, I decided to figure out how to use the official microsoft/playwright-mcp Playwright MCP server with Claude Code. …

Converting ORF raw files to JPEG on macOS

One of our cameras takes raw photos in ORF format, which I believe stands for "Olympus Raw Format". Here's a recipe I found for converting them to JPEG on macOS: …

Publishing a Docker container for Microsoft Edit to the GitHub Container Registry

Microsoft recently released Edit, a new terminal text editor written in Rust. It's pretty nice - it's reminiscent of nano but with a retro MS DOS feel. …

Redirecting a domain using Cloudflare Pages

I wanted to redirect https://global-power-plants.datasettes.com/ to https://datasette.io/ - I decided to spin up a Cloudflare Pages site to do the work. …

A tip for debugging pytest-httpx

I use pytest-httpx in a bunch of my projects. Occasionally I run into test failures like this one, which can sometimes be really hard to figure out: …

SQLite triggers

I wrote a Python script, triggers.py, to help me understand what data is available to SQLite triggers for which operations. …

Building and deploying a custom site using GitHub Actions and GitHub Pages

I figured out a minimal pattern for building a completely custom website using GitHub Actions and deploying the result to GitHub Pages. …

Styling an HTML dialog modal to take the full height of the viewport

I've been experimenting with the HTML dialog element recently, for example in my Prompts.js JavaScript library. …

Running jupyterlab via uv tool install

I tried to get jupyterlab working via uv tool install today and ran into some sharp edges. …

Using a Tailscale exit node with GitHub Actions

For an (ethical) scraping project I found that my low-volume scraper was working from my laptop but was being blocked by Cloudflare when I attempted to run it in GitHub Actions, presumably because the GitHub Actions IP range was disallowed. …