just what i find

Archives 2026

Responsible disclosure: TRMNL MCP server leaks password fields to AI agents

TRMNL is an awesome open-source ePaper display that lets users show almost anything they want. Launched on Kickstarter, it’s now got thousands of plugins and users. I’ve published a few plugins, including a realtime TfL bus stop arrival time board.

A few weeks ago, TRMNL added an MCP server. This lets you use a local coding agent to do most of the grunt work. I wrote about how to set that up in another post.

Using multiple TRMNL MCP API keys in a single plugin repo

TRMNL is an awesome open-source ePaper display that lets users show almost anything they want. Launched on Kickstarter, it’s now got thousands of plugins and users. I’ve published a few plugins, including a realtime TfL bus stop arrival time board.

A few weeks ago, TRMNL added an MCP server. This lets you use a local coding agent to do most of the grunt work.

Install the MCP server through .mcp.json

Using Claude Code, i created a simple .mcp.json file which references the API key by environment variable. Place this in the root of your repo. As it contains no secrets, this is safe to commit:

Fix: Claude Code with op run responds "No deferred tool marker found in the resumed session"

To stop my API keys being sucked up by some random malware, I keep secrets in 1Password and inject them into commands at runtime with op run. That works for almost everything, but when using Claude Code, i started getting a confusing error message.

Injecting MCP API keys to Claude Code

Claude Code reads MCP server config from .mcp.json and expands ${VAR} references from the environment.

{
  "mcpServers": {
    "trmnl": {
      "type": "http",
      "url": "https://mcp-server.com/mcp?api_key=${MY_SECRET_MCP_API_KEY}"
    }
  }
}

The API key is in a file. .env.tpl:

Recipe: Veggie smoked beans

Ingredients

  • 1 tin baked beans
  • 1 small white onion
  • 1 clove garlic
  • 1 tbsp Hendersons Relish
  • splash of vegetable oil

Method

  1. finely chop the onion
  2. grate the garlic
  3. gently (low temperature) fry onion in the vegetable oil for 5-6 mins over a medium heat in the small (metal?) roasting tin you’ll use in the smoker
  4. Take off the heat and add the grated garlic
  5. Add the beans and hendersons
  6. Stir and put in the smoker (110-120c) for 60-90 minutes. Check after 60 mins to make sure not dried out. If it starts to get dry, add a splash of water. Stir.

Recipe: Spicy cheese quesadillas

ingredients

  • 20g cheddar
  • 20g light cream cheese
  • 1 tsp paprika
  • 1/2 tsp chilli flakes
  • 30g tinned sweetcorn, drained well
  • One of:
    • 1/2 tsp miso paste
    • 1 tsp soy sauce
    • 1 tsp Hendersons relish
  • 1/4 tsp garlic powder
  • 1/4 tsp onion powder
  • pinch white pepper
  • 1 small tortilla

Method

  1. Mix cream cheese & miso/soy/Henderson’s + spices into a paste
  2. Fold in sweetcorn and cheddar
  3. Spread in tortilla and fold in half
  4. Cook in pan, place baking parchment on top and plate on that to squash slightly

Recipe: Smoked Tempeh

Smoked tempeh is excellent for smoking as the dense, porous structure absorbs smoke like few other proteins.

Ingredients

  • 30ml soy sauce
  • 3 grams (about 1-2 cloves) minced garlic
  • 15 g sesame oil
  • 15ml rice vinegar - needed to penetrate
  • pinch of salt

Method

  1. Slice into planks or thick strips rather than cubes — more surface area, better bark formation, less risk of drying out.
  2. Steam or simmer the tempeh for 10-15 minutes first. This removes bitterness and opens the pores for better smoke and marinade absorption.
  3. Marinate for at least 30 minutes. This gives time for the marinade to to infuse.
  4. Smoke at 110-130°C for 45-60 minutes.
  5. Baste or re-glaze halfway through (or more often if you like) if using a sticky marinade.

Recipe: Smoked Roast Potatoes

Ingredients

  • as many New Potatoes as you think you’ll need
  • Vegetable oil
  • salt, pepper
  • and a smoker

Method

  1. Boil new potatoes until fork-tender (15-20 min) then drain
  2. smash them on a sheet tray to break them slightly. The fluffy bits go crispy.
  3. Coat them in oil/fat (e.g. drizzle in vegetable oil) and seasoning (salt, pepper)
  4. roast in the oven at 220°C for 20-25 minutes until the edges are crispy and golden.
  5. Transfer to the smoker at 110-130°C for 20-30 minutes to pick up smoke flavour. The already-crisped surfaces absorb smoke well. Going longer than 30 minutes risks drying them out or making the smoke flavour acrid.

Updated: using 1Password and direnv to store developer secrets

I previously wrote about how i’d implemented a combination of 1Password’s CLI and direnv to avoid storing passwords on disk. This works great, most of the time. The problem comes when i want to add a secret and i’m in the middle of a complex multi-agent task - because the environment is loaded once before Claude or Codex or whatever starts, adding or rotating credentials during a build (e.g. if the agent needs an API key to a new service, or accidentally exposes a secret to its context) is tricky. So i’ve evolved the system slightly.

Recpie: Miso BBQ Aubergine

glaze

  • 30g white or red miso (white = sweeter/milder, red = deeper/saltier)
  • 15ml soy sauce
  • 15ml mirin (or 10ml rice vinegar + small pinch sugar)
  • 10g grated garlic
  • 10g sesame oil
  • 15ml honey
  • optional: 5g grated ginger

Method

  1. Halve aubergines lengthways. Score the cut face in a crosshatch ~1cm deep without piercing the skin. This speeds cooking and lets glaze penetrate.
  2. Lightly oil the cut face only. Do not soak it — aubergine absorbs oil indefinitely. Use high temperature oil (vegegable or avocado oil, not olive)
  3. Grill cut-side down over medium heat 6-8 minutes until charred and the flesh is collapsing soft. Flip to skin-side down for another 4-6 minutes.
  4. Test doneness: the flesh should be fully translucent and yielding throughout, no white spongy core.
  5. Brush glaze onto the cut face. Return cut-side down over medium-low heat 2-3 minutes to caramelise. Watch closely — it scorches quickly.
  6. Re-glaze and finish skin-side down 1-2 minutes to set the lacquer without burning.

Minimum Viable Solution Architecture

Recently at work, someone asked me why my team wasn’t creating detailed logical data models as part of their solution design. It seems quite simple to me – they don’t do it because nobody reads it. Most solution architecture documentation is out of date before it even gets logged in the architecture repository.

Why do we even write documentation?

I think we create documentation for one of two reasons:

  1. Collaborate now: as soon as there is a second person in a team, they need to start talking so that they can work together. The more people, the harder this is (which is why we try to limit teams to half a dozen people or so). Documentation is a great way for people to collaborate. When things are written down, everyone can see it, and by adding comments or questions we get to a resolved version.
  2. Communicate with the future: We make decisions today which are the best available given the information we have at the time. Sometimes we forget what we decided, or why, and documentation gives us that. It’s not about proving whether a decision was right or wrong – that’s largely irrelevant – it’s about understanding what led us to that decision in the first place. Perhaps the reasons are still valid and we just forgot what they were. Or perhaps information we have now changes things.

So really, design documentation is about ensuring that everyone’s on the same page, both now and in the future. Some designs are ephemeral – just to help us get our heads around what we want to do, and some is persistent – giving us a framework on which to hang our future plans.

Using 1Password and direnv to store developer secrets

This is more notes to myself so i don’t forget. Goals:

  • no secrets stored on the filesystem (so they can’t be sucked up by credential-harvesting malware)
  • individual vault per project
  • secrets automatically injected to envvars so that apps can follow 12-Factor App methodology
  • deployed secrets use platform native secret manager (e.g. Cloudflare secrets manager, Azure keyvault, AWS SSM Parameter Store etc.)
  • separation of this vault from my main password vault in BitWarden. My whole world exists in Bitwarden - i dont want to inadvertently expose it. And as Bitwarden has a pretty awful CLI experience, it doesnt work for me.
  • Safe to commit secret file to source control
  • Safe to commit my .env files to source control because they no longer contain secrets

Benefits of 1Password:

Using Web Search with Claude Code API Billing

If you’re running Claude Code against your own API key or through a proxy instead of Anthropic’s native backend, you’ve probably noticed that the built-in WebSearch tool just doesn’t return results because it relies on a server-side API that only exists at Anthropic. Without web search, the agent cant research, so we built a self-hosted replacement using MCP.

What we built

The brilliant DDGS is an open source metasearch server that aggregates results from DuckDuckGo, Bing, Google, Brave, and Yahoo. It has a built in MCP server which exposes five tools: search_text, search_news, search_images, search_videos, and search_books.

Using Claude Code for things that aren’t code – gardening

Claude Code can do a lot of things. But can it keep a plant alive?

The Plant: Act I — Fifty-Eight Days

October 22 – December 19, 2025

On October 22, 2025, the moisture sensor read 1829, the first of 5,568 check-ins over fifty-eight days. The scale runs from wet at roughly 1100 to dry at 3400, putting that reading in the middle.

The sensor is in the pot of a Tradescantia zebrina. There’s also a 5l jug of water with a pump, a grow light and a camera. Claude’s job is to keep the plant alive - basically, keep the moisture in range and run the grow light on schedule, writing notes after each cycle so it could pick up where it left off. At the start, there were five unknowns: plant species, target moisture range, soil type, ambient temperature, and pot size. Claude logged all five, ran a 60-minute grow light session, and began monitoring. The full session logs, sensor data, and photos are at plants.cynexia.com if you want to follow along.

Auto-switching SSH keys for work repos

I have a personal GitHub account and a corporate one, and I found it annoying to have to select the correct SSH key for work repos, so I configured git and SSH to pick the correct key for me. After initial setup, there are three config files to modify:

After the initial setup, there are three config files involved:

  1. Git URL rewrite (~/.gitconfig) — rewrites work org URLs to an SSH host alias
  2. SSH host alias (~/.ssh/config) — maps that alias to the right key (and port 443 if needed)
  3. Work-only Git identity (~/.gitconfig.work) — sets work email/name/signing key, loaded automatically for work repos

How does it work? well