Serve an Updating Image as an MCP Resource on macOS — and Where You Can Actually See It

A resource that returns a picture is the most demanding shape an MCP resource takes. It has to survive base64 encoding, arrive with a MIME type the client will accept, stay small enough to attach, and — if the picture is meant to reflect something live — return different bytes the next time the same URI is read. Serve Resources Well from an MCP Server on macOS covered the shapes of resources with an eight-byte PNG magic number standing in for a binary body. This article replaces that stub with a real image: a bar chart rendered on every read from state that anything can change, packaged so uvx runs it from a local directory, and wired into opencode so a model can read it, watch it change, and read it again. ...

69 min

Add an MCP Server to opencode on macOS

opencode attaches an MCP server in a single command, with no config file to hand-edit and no JSON to get wrong. Everything after the -- separator is the command that launches the server, and because uvx resolves the package on first launch, there is nothing to install beforehand either: opencode mcp add hello -- uvx mcp-hello-server This tutorial uses that command to attach mcp-hello-server — a small FastMCP server on PyPI exposing two tools, server_info and greet — then drives it from a free model. You will confirm the server connects, prove the launch command works without involving a model at all, pin the same server to a single project instead of your whole machine, and finally ask North Mini Code Free to say hello to Alice in Japanese. ...

16 min

Build a Safe Read-Only SQLite MCP Server on macOS

The read-only Postgres MCP server built its safety on the database server: a SELECT-only role, a read-only transaction, a statement timeout, all enforced by a process on the other side of a socket. SQLite has none of that machinery. There is no server, no accounts, no GRANT. The database is a file, the engine runs inside your process, and any connection that can open the file read-write can do anything to it. ...

29 min

Build a Safe Read-Only Postgres MCP Server on macOS

Give a language model a database tool and the obvious failure mode is not a clever exploit. It is a well-meaning model running DELETE FROM orders because a prompt told it to “clean up test data,” or issuing SELECT * FROM events against a billion-row table and stalling everything behind it. A database MCP server has to assume the caller is careless, and sometimes hostile. In this tutorial you will build a FastMCP server that exposes a Postgres database to an MCP client as strictly read-only, with the safety built in layers so no single mistake opens a hole: ...

27 min

Dockerize an MCP Server on macOS

Packaging an MCP server as a Docker image gives clients one launch command with no Python, no uv, and no virtual environment to manage on the host: they run the container. This tutorial builds a small, non-root image for a FastMCP server with a multi-stage build, runs it over stdio the way a client does, and wires it into a client’s .mcp.json with docker run. The build uses uv in the builder stage and copies only the finished virtual environment into a slim runtime stage, so the final image carries the app and its dependencies — not the build toolchain. ...

14 min

CI/CD for an MCP Server: Lint, Test, Build, and Publish with GitHub Actions on macOS

An installable MCP server needs the same release discipline as any Python package: lint and test every change, build the artifact, and publish on a tagged release. This tutorial wires that pipeline with GitHub Actions and uv — ruff for linting and formatting, pytest for tests, uv build for the wheel/sdist, and PyPI trusted publishing (OIDC, no stored token) on a version tag. It is the CI/CD companion to Publish a FastMCP Server to PyPI and Run It Anywhere with uvx. ...

13 min

Harden an MCP Server: A Threat Model and Defenses on macOS

An MCP server is an attack surface. It runs with real privileges (a filesystem, API credentials, a database), it accepts arguments chosen by a model that may be under an attacker’s influence, and its results flow straight back into a model’s context. Most MCP defenses are the server author’s responsibility — the client cannot enforce them for you. This tutorial builds one hardened FastMCP server, a sandboxed “knowledge base,” and demonstrates a defense for each of the common MCP threats: input validation, the confused-deputy problem, prompt injection carried in tool results, tool poisoning, and the token-passthrough anti-pattern. Every defense is backed by a test, so you can see it hold. ...

17 min

Debug an MCP Server with the MCP Inspector on macOS

The MCP Inspector is the official developer tool for MCP servers. It has two faces: a web UI for clicking through tools, resources, and prompts interactively, and a CLI mode that drives the same server headlessly and prints JSON — scriptable, diffable, and easy to drop into a Makefile or CI. This tutorial uses both against a small FastMCP server, then reproduces and diagnoses a broken stdio handshake, the most common failure when a client refuses to connect. ...

13 min

Build an MCP Server in Rust with the Official SDK

This is the Rust companion to Build an MCP Server in Go with the Official SDK and Build an MCP Server in TypeScript with the Official SDK. It builds the same kind of server — two tools and a resource — with the official Rust SDK (rmcp), served over the stdio transport so a local client launches it as a subprocess. The result is a single compiled binary with no runtime beyond itself. ...

18 min

Fine-Grained Authorization for a FastMCP Server on macOS

An email allowlist answers one question: is this caller allowed in at all? Real servers need a second answer: which of my tools may this particular caller use? A reader should be able to list notes but never delete one; an admin should be able to delete anyone’s. That is authorization, and FastMCP gives you two levers for it: a declarative require_scopes(...) on each tool, and the caller’s identity inside a handler for checks a static scope cannot express. ...

17 min