Skip to main content
Dev Sac
API

MCP Servers | Stock Images and WordPress Tooling for AI Agents

Two Model Context Protocol servers built to give Claude Code safe, typed access to external systems. A stock image server with a multi-provider waterfall and an SSRF-hardened download tool, and a WordPress bridge with rails that make destructive edits structurally impossible.

TypeScript Node.js MCP SDK Zod Vitest
0
MCP Tools
0
Servers
0
External APIs
0
Publish Paths

Why write your own

An MCP server is how you give an AI agent hands. The protocol itself is straightforward. The engineering is entirely in deciding what the agent is allowed to touch, and making the wrong thing impossible rather than merely discouraged.

That is the argument for building these instead of installing them. A third-party server is a set of capabilities someone else scoped for their risk tolerance, running with your credentials against your production systems. When the tool is going to be called autonomously, hundreds of times, by something that does not get tired or nervous, the scoping decisions are the product.

These are two of the three MCP servers I have written. The third, Amazon Creators API, has its own page.

Stock Images: a waterfall with a hardened download

The stock image server replaces a third-party Pexels server at feature parity and adds a second provider plus a download tool. Nine tools: unified photo and video search, per-source lookups, the curated_photos and popular_videos feeds, collections, and a download.

Search runs as a waterfall. Pexels answers first, and Pixabay only supplements when Pexels underfills the request. The unified response carries the results alongside which sources succeeded and any warnings, so a single failing provider degrades to a warning instead of failing the whole call. The request only errors when every provider is down.

That shape matters for autonomous use. An agent sourcing an image mid-article should not have its work abandoned because one API had a bad minute, and it also should not silently receive half the results it asked for with no signal that something went wrong.

search _photos Pexels primary · ~200/hr only if underfilled Pixabay supplement { results, sourcesOk, warnings } partial failure is visible ONE PROVIDER DOWN IS A WARNING, NOT AN ERROR

The download tool is the dangerous one

Every other tool on that server reads from an API. The download tool writes to disk from a URL, which makes it the one place an agent could genuinely cause damage, and it is scoped accordingly.

It only fetches HTTPS URLs on known Pexels and Pixabay CDN hosts, and it revalidates that on every redirect hop rather than trusting the initial URL. Content type has to be an image or video. Size is capped. Writes are confined to a single configured directory with no traversal, no absolute paths, and no symlink escape.

The tool is also disabled entirely unless that download directory is explicitly configured. There is no default. If nobody made a deliberate decision about where files land, the answer is that the agent cannot write files.

Every search result also carries a paste-ready attribution string, because crediting the photographer is a condition of Pexels API access. Making attribution a property of the result rather than a step someone has to remember is the difference between a license term that is respected and one that is respected when convenient.

WordPress: rails against the thing you actually fear

The WordPress bridge wraps a live site’s REST API in seven tools: get a post, list posts, create a draft, update a post, upload media, upload and attach a featured image in one call, and run a registered ability.

The interesting part is what it refuses to do. Publish dates are stripped from every create and update payload unconditionally, so an agent cannot silently rewrite the publish date of an article with years of accumulated search equity. New posts default to draft. Nothing this server does results in unreviewed content appearing on a live site.

Those two rails exist because they map to the failure modes that would actually hurt: an agent republishing an old post to the top of the feed, or pushing a half-finished draft live. Both are recoverable and both are the kind of thing you would rather never explain to anyone. The protection is structural, applied in the request layer, and does not depend on prompt instructions holding.

agent sends title content date status: publish request layer strips date forces draft unconditionally WordPress gets title content status: draft no date field THE DESTRUCTIVE PATH IS REMOVED, NOT DISCOURAGED

Credentials load from a project-local environment file at startup and never appear in any committed configuration.

Scope discipline

The bridge used to carry more tools. Customizer CSS, theme elements, and block collection tools were removed once a companion WordPress plugin could serve them as typed tools from inside WordPress itself, where they belong.

Deleting working code from your own tool because something else is now a better home for it is the habit that keeps an agent’s tool surface honest. Every tool in the list is a thing the model has to consider on every call, and a server that accumulates capabilities it no longer needs makes the agent worse at using the ones that matter.

Testing

Both servers ship tests. The stock image server runs a Vitest unit suite over the merge and attribution logic. The WordPress bridge uses characterization tests against recorded API responses, which is the appropriate technique when the thing under test is a wrapper around somebody else’s API: you are not verifying that WordPress works, you are verifying that your translation layer keeps behaving the same way after you change it.

The broader pattern

Every one of these decisions is the same decision in different clothes. Give the agent the narrowest capability that does the job, make the destructive paths structurally unreachable rather than merely unlikely, and surface partial failure instead of hiding it.

This is the same practice described in AI Architecture in Practice, applied at the tool boundary. If you are wiring an AI agent into systems that matter, that boundary is where the engineering is.

Like what you see?

I build tools that solve real problems. If you have an idea or a project that needs engineering, let's talk.

Get in Touch