Professional notes by Craig Johnston
long-form, short-form, working drafts · since 2008
long-form, short-form, working drafts · since 2008
VOL. XIX · MMXXVI
106 NOTES IN PRINT
106 NOTES IN PRINT
Build a Better Platform Than You're Renting
Cloud-grade data platforms from FOSS on Kubernetes, now that agents run the hard part
→ Read this noteLatest note · folio diagram
mindmap
root((Self-hosted platform))
Instead of Snowflake
Trino plus Iceberg
object storage
Instead of managed search
OpenSearch
Instead of Confluent
Kafka with KRaft
Instead of Workato
Apache NiFi
Instead of managed databases
Postgres
Cassandra
Operated by
Kubernetes
AI agents
In 2020, Apress published my book, Advanced Platform Development with Kubernetes. It runs about five hundred pages and builds a complete data-centric platform end to end: streaming ingestion, search and analytics, data …
Recent Notes
CV
A Complete MCP Server and Client in Go
One server that uses every primitive, runs over both transports, sits behind auth, and is tested end to end, with one annotated trace through all of it
2026-06-08
CIVRunning Ollama and pgvector on Kubernetes
Vector Search Without the Vector Database, Part 3
2026-06-07
CIIITasks: Durable, Async Tool Calls
The experimental 2025-11-25 primitive that turns a request into call-now-fetch-later, hand-rolled on the wire because the SDK has not caught up
2026-06-06
CIISecurity: Tool Poisoning, Prompt Injection, and the Trust Boundary
A server's words flow straight into the model's context. The anatomy of how that becomes an attack, and where the host has to stand to stop it
2026-06-04
CIAuthorization: OAuth 2.1 for HTTP MCP Servers
How a remote server proves who is calling: the discovery chain, resource indicators that bind a token to one server, and the passthrough ban that stops a confused deputy
2026-06-02
CLogging and the Notification Family
Structured server logs filtered by severity, and the list_changed notifications that keep a client's view of a server from going stale
2026-05-31
XCIXProgress, Cancellation, Ping, Pagination, and _meta
The plumbing every MCP primitive shares: how a long call reports progress, how either side cancels one, how a connection is checked, how a list is paged
2026-05-29
XCVIIIElicitation: When the Server Needs to Ask the User
The server asks a person for input mid-task, with a flat form for ordinary data and a URL handoff for the secrets a form must never touch
2026-05-27
XCVIIRoots: Telling the Server Where It May Look
The client hands the server a list of directories it is allowed to use, a boundary the server asks for and the client defines and enforces
2026-05-25
XCVSampling: When the Server Calls Your Model Back
The protocol inverts. A server asks the client to run the host's model, with no API key of its own and a human able to deny every request
2026-05-23
XCVIThe Embedding Pipeline on Postgres and Ollama
Vector Search Without the Vector Database, Part 2
2026-05-23
XCIVCompletion: Argument Autocomplete for Prompts and Resources
One method that suggests values as a user fills in an argument, and the context field that scopes those suggestions to what is already filled
2026-05-21
XCIIIPrompts: Server-Authored Conversation Starters
The third server primitive and the one people misread: templates the user selects, rendered into messages that can embed a server's own resources
2026-05-19
XCIIResources: list, read, templates, and subscriptions
The primitive for data the model reads, the counterpart to tools, with URI templates for whole families and live notifications when something changes
2026-05-17
XCITools, Part 2: Calling, Content Types, and Structured Output
Every shape a tool result can take: the five content types, structured output validated against a schema, and the difference between a failed tool and a failed protocol
2026-05-15
XCTools, Part 1: Discovery and the inputSchema Contract
What the model actually receives when it lists a server's tools, how the schema is built from a Go type, and why a tool's annotations cannot be trusted
2026-05-13
LXXXIXTransport II, Streamable HTTP: One Endpoint, Two Directions
A single HTTP path carries the whole protocol, a session id threads state across requests, and a dropped stream resumes where it left off
2026-05-11
LXXXVIIITransport I, stdio: Two Pipes and a Subprocess
The simplest MCP transport, built by hand on both sides with no SDK, plus the stderr rule that silently breaks servers
2026-05-09
LXXXVIIWhy Data MCPs Need Vector Search
Vector Search Without the Vector Database, Part 1
2026-05-08
LXXXVIThe Handshake: initialize, Capabilities, and Version Negotiation
The one exchange that must happen before any other MCP method is legal, hand-rolled in Go against a real server
2026-05-07
LXXXVJSON-RPC 2.0: The Grammar Underneath MCP
Three message shapes, one error object, and a hundred and fifty lines of Go that every MCP transport is built on
2026-05-05
LXXXIVWhy a Protocol, Not an API
Build the same tool as a REST endpoint and as an MCP server, then watch what discovery, schemas, and the callback direction cost to hand-roll
2026-05-03
LXXXIIIWhy Read the Wire When the SDK Hides It
Drive a real Go MCP server with four lines of JSON and watch the protocol the SDK keeps out of sight
2026-05-01
LXXXIIThe Pre-Commit Review Gate
AI on a Leash: Mechanical Self-Review for Claude Code
2026-04-22
LXXXIThe Two Failure Modes That Break Your AI Data Agent
A Case Study in Agent Psychology
2026-02-26
LXXXPostgreSQL to OpenSearch with PySpark on Kubernetes
Date-windowed ETL, idempotent upserts, and CronJob scheduling
2026-02-25
LXXIXGoReleaser with Cosign Signing and Syft SBOM
Signed Builds and Supply Chain Security for Go Projects
2026-02-11
LXXVIIIAI on a Leash: Complete Go Project Configuration
AI on a Leash for Go
2026-02-09
LXXVIIRalph's Uncle
AI on a Leash
2026-02-06
LXXVIGo's Constraints and Idioms Make AI Coding Better
From Vibe Coding to Vibe Engineering
2026-02-04