Architecture¶
Deliberately small: three source files, no state, no magic.
src/
index.ts MCP server — tool/prompt registration, stdio transport
client.ts Typed client for the Imgflip REST API
types.ts Shared type definitions for API payloads
Request flow¶
sequenceDiagram
participant C as MCP Client<br/>(Claude, Copilot, …)
participant S as imgflip-mcp<br/>(stdio subprocess)
participant A as api.imgflip.com
participant I as i.imgflip.com
C->>S: tools/call caption_image
S->>S: validate input (zod), check captions present
S->>A: POST /caption_image (form-urlencoded)
A-->>S: { url, page_url }
S->>I: GET generated image (≤2 MB, best-effort)
I-->>S: image bytes
S-->>C: text (JSON) + inline image block
Design decisions¶
Thin, faithful API mapping. Tool names and parameters mirror the Imgflip endpoints one-to-one. Anyone who has read imgflip.com/api knows this server, and vice versa.
Premium is opt-in at registration time. Rather than exposing seven tools and letting five of them fail on free accounts, the server registers Premium tools only when IMGFLIP_PREMIUM=true. Clients see a tool list that is guaranteed to work.
Errors are data, not crashes. Every Imgflip failure (API error message, HTTP error, timeout, missing credentials) becomes a readable isError tool result. The model can react — retry, rephrase, tell the user — instead of the server dying.
Image embedding is best-effort. The generated meme is downloaded and attached as an inline MCP image block when it's ≤2 MB and the download succeeds; otherwise the URL alone is returned. A slow CDN never fails a meme.
Stateless by construction. No cache, no disk writes, no sessions. Every tool call is independent, which keeps the security story one sentence long: credentials in, HTTPS to Imgflip, nothing retained.
Key implementation details¶
- Transport: stdio via
@modelcontextprotocol/sdk(McpServer+StdioServerTransport) - Validation: zod schemas per tool; the SDK converts them to JSON Schema for clients
- HTTP: native
fetch(Node ≥ 18) with a 30 sAbortSignal.timeout - Form encoding:
URLSearchParams, including Imgflip'sboxes[i][field]array convention - Version: read from
package.jsonat runtime — single source, synced to all metadata files byscripts/sync-versions.mjs