Project 02·AI Infrastructure·2026

Your images,
one sentence
away from AI.

A production Model Context Protocol server that connects directly to Anthropic's Claude, so any image in your library is one natural-language request away, backed by real OAuth, a database, and cloud object storage.

Role
Full-stack developer
Type
Hosted, multi-tenant MCP
Stack
Clerk · Supabase · Cloudflare R2 · Railway

The idea

What is MCP?

The Model Context Protocol is the standard way to give an AI assistant a new ability by connecting it to an outside service. Think of how Claude can be connected to Google to search the web or read your Drive. That connection is an MCP. The protocol is the bridge, and each service plugs into one side of it.

I built the same kind of bridge, but for images. My MCP connects Claude to a real image library, so instead of reaching Google, Claude can reach your photos. It exposes tools like search_images, list_images, upload, and manage. You ask in plain English, "find the product shots tagged hero, landscape," and Claude queries your library and returns them mid-conversation.

From prototype to product

  1. Started as a local stdio server

    The first version ran locally over stdio, enough to prove the tools worked, but single-user and tied to my machine.

  2. Added real authentication

    Clerk OAuth with opaque bearer tokens, so the server is multi-tenant: every request is scoped to the user who owns the library, not a shared key.

  3. Real metadata storage

    Supabase (PostgreSQL) with GIN-indexed tag search, so searching by purpose and tags stays fast as the library grows.

  4. Real file storage

    Cloudflare R2 (S3-compatible) object storage holds the image files themselves, separate from the metadata database.

  5. Hosted and reachable by Claude

    Served over a Streamable-HTTP transport on Railway, with a Netlify landing page. The result: a production system demonstrating OAuth, a database, and object storage working together behind a widely-trusted AI platform.

What a request actually looks like

One natural-language ask, end to end

When you ask Claude to find an image, it never touches your files directly. It calls a tool on the server with your search terms. The server checks your bearer token through Clerk, looks up matching rows in Supabase using the tag index, and hands back links to the files in Cloudflare R2. Claude receives a small structured result it can show inline, mid-conversation.

Nothing about the library is baked into the model. The server is the single source of truth, and every request is scoped to the user who owns it, so two people can use the same server and only ever see their own images.

Splitting metadata from files is a deliberate choice. A database is fast for searching tags and purposes but a poor place to store large image blobs, so the tags live in Postgres and the files live in object storage. That keeps search quick and storage cheap as the library grows.

Fetched live through the MCP

These images were pulled straight from my library by Claude, in conversation, through the image-library MCP. I asked for them by purpose and tag and the server returned the files.

A sunflower photograph retrieved from the image library via the MCP
Fetched via image-library MCP
A chameleon photograph retrieved from the image library via the MCP
Fetched via image-library MCP
An elephant photograph retrieved from the image library via the MCP
Fetched via image-library MCP

Retrieved with a single natural-language request to Claude, which called search_images on the server and returned these results inline.

The stack, in production

Four services do the real work behind the server, auth, metadata, file storage, and hosting. These are screenshots from the live consoles.

Railway dashboard, image-mcp-server deployment online
Railway, hosting
Supabase storage bucket holding the image files
Supabase, database
Cloudflare dashboard for the R2 object storage and account
Cloudflare R2, files
Clerk authentication dashboard showing active users
Clerk, OAuth

Railway hosts the server, Supabase (PostgreSQL) stores tagged metadata, Cloudflare R2 holds the image files, and Clerk handles OAuth, each scoped per user.

See it live

pixlib.appOpen ↗

Embedded live. If it doesn't load, open it in a new tab via the link above.

What I learned

Taking something from "works on my laptop" to "a stranger can sign in and use it safely" is where most of the real engineering lives. Auth scoping, indexing for search, and splitting metadata from blobs are decisions you don't feel in a prototype but feel immediately in production.

If you're learning this: build a tiny MCP server locally first and connect it to Claude before adding any infrastructure. Once the tool contract is solid, layer in auth, then a database, then storage, one concern at a time.

Model Context ProtocolClerk OAuthSupabase / PostgreSQLGIN indexCloudflare R2Streamable-HTTPRailwayNetlify
PreviousZero-Knowledge Proof