AI & engineering
Building a meme generator MCP server from scratch
What changes when Claude can actually use an API? Build a meme generator to see how an MCP server connects conversation to action.
Originally published on Medium on 2025-05-25. This is the original May 2025 tutorial. The code uses an older MCP SDK interface and has not been retested against current SDK releases.
What is MCP and Why Should You Care?
Think of MCP (Model Context Protocol) as a bridge that connects AI assistants like Claude to external tools and data sources. Instead of Claude being limited to just text conversations, MCP lets it interact with APIs, databases, and other services in real-time. Imagine asking Claude to “create a funny meme about cats” and having it actually generate the meme for you, rather than just giving you instructions. That’s the power of MCP — it turns AI assistants into action-takers, not just advice-givers.
Why MCP is Revolutionary
Before MCP, if you wanted Claude to help with specific tasks like:
- Creating memes
- Checking weather data
- Managing your calendar
- Analyzing your files
You’d have to manually copy-paste information back and forth. With MCP, Claude can directly access these services and perform actions for you.
Building Your First MCP Server: A Meme Generator
Let’s build something fun — a meme generator that connects to the Imgflip API. By the end of this tutorial, you’ll be able to ask Claude to create memes directly in your conversation.
Step 1: Install the Python MCP Client
First, you need to install the MCP tools on your computer. Open your terminal or command prompt and run:
pip install mcp requests
This installs the MCP client that lets you build and test your server.
Step 2: Create Your MCP Server
Now let’s build the core functionality. Create a new Python file called imgflip_mcp_server.py:
#!/usr/bin/env python3
import asyncio
import json
import requests
from typing import Any, Dict, List
from mcp.server.models import InitializationOptions
from mcp.server import NotificationOptions, Server
from mcp.server.models import (
CallToolResult,
EmptyResult,
GetPromptResult,
ListPromptsResult,
ListResourcesResult,
ListToolsResult,
ReadResourceResult,
TextContent,
Tool,
)
from mcp.types import (
CallToolRequest,
GetPromptRequest,
ListPromptsRequest,
ListResourcesRequest,
ListToolsRequest,
ReadResourceRequest,
)
import mcp.types as types
# Create the MCP server
server = Server("imgflip-mcp-server")
# Store Imgflip credentials (you'll need to sign up at imgflip.com)
IMGFLIP_USERNAME = "your_username" # Replace with your Imgflip username
IMGFLIP_PASSWORD = "your_password" # Replace with your Imgflip password
@server.list_tools()
async def handle_list_tools() -> ListToolsResult:
"""Tell Claude what tools are available"""
return ListToolsResult(
tools=[
Tool(
name="get_popular_memes",
description="Get a list of popular meme templates that can be used to create memes",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
),
Tool(
name="create_meme",
description="Create a meme using a template ID and text",
inputSchema={
"type": "object",
"properties": {
"template_id": {
"type": "string",
"description": "The ID of the meme template to use"
},
"top_text": {
"type": "string",
"description": "Text to display at the top of the meme"
},
"bottom_text": {
"type": "string",
"description": "Text to display at the bottom of the meme"
}
},
"required": ["template_id", "top_text"]
}
),
Tool(
name="search_memes",
description="Search for meme templates by keyword (requires premium account)",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search term to find meme templates"
}
},
"required": ["query"]
}
)
]
)
@server.call_tool()
async def handle_call_tool(request: CallToolRequest) -> CallToolResult:
"""Handle when Claude wants to use one of our tools"""
if request.params.name == "get_popular_memes":
try:
response = requests.get("https://api.imgflip.com/get_memes")
data = response.json()
if data["success"]:
memes = data["data"]["memes"][:10] # Get top 10 memes
meme_list = []
for meme in memes:
meme_list.append({
"id": meme["id"],
"name": meme["name"],
"url": meme["url"]
})
return CallToolResult(
content=[TextContent(
type="text",
text=f"Popular meme templates:\n{json.dumps(meme_list, indent=2)}"
)]
)
else:
return CallToolResult(
content=[TextContent(
type="text",
text="Failed to fetch popular memes"
)]
)
except Exception as e:
return CallToolResult(
content=[TextContent(
type="text",
text=f"Error fetching memes: {str(e)}"
)]
)
elif request.params.name == "create_meme":
try:
template_id = request.params.arguments.get("template_id")
top_text = request.params.arguments.get("top_text", "")
bottom_text = request.params.arguments.get("bottom_text", "")
payload = {
"template_id": template_id,
"username": IMGFLIP_USERNAME,
"password": IMGFLIP_PASSWORD,
"text0": top_text,
"text1": bottom_text
}
response = requests.post("https://api.imgflip.com/caption_image", data=payload)
data = response.json()
if data["success"]:
meme_url = data["data"]["url"]
return CallToolResult(
content=[TextContent(
type="text",
text=f"Meme created successfully! View it here: {meme_url}"
)]
)
else:
return CallToolResult(
content=[TextContent(
type="text",
text=f"Failed to create meme: {data.get('error_message', 'Unknown error')}"
)]
)
except Exception as e:
return CallToolResult(
content=[TextContent(
type="text",
text=f"Error creating meme: {str(e)}"
)]
)
elif request.params.name == "search_memes":
return CallToolResult(
content=[TextContent(
type="text",
text="Meme search requires a premium Imgflip account. Please upgrade to use this feature."
)]
)
else:
return CallToolResult(
content=[TextContent(
type="text",
text=f"Unknown tool: {request.params.name}"
)]
)
async def main():
# Import here to avoid issues with event loops
from mcp.server.stdio import stdio_server
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="imgflip-mcp-server",
server_version="0.1.0",
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
),
),
)
if __name__ == "__main__":
asyncio.run(main())
Important Setup Notes:
- Get Imgflip Account: Sign up for a free account at imgflip.com
- Add Your Credentials: Replace
your_usernameandyour_passwordwith your actual Imgflip login details - Save the File: Save this code as
imgflip_mcp_server.pyin a new folder
Step 3: Test Your MCP Server
Now let’s test if your server works. In your terminal, navigate to the folder where you saved the file and run:
python imgflip_mcp_server.py
If everything is set up correctly, your MCP server will start running and wait for connections. To test it with the MCP inspector (a visual testing tool), run this in a new terminal window:
npx @modelcontextprotocol/inspector python imgflip_mcp_server.py
This opens a web interface where you can test your tools:
- Try the “get_popular_memes” tool to see available meme templates
- Use “create_meme” with a template ID and your own text
- Watch as your server generates real memes!
Step 4: Connect to Claude Desktop
Now for the exciting part — connecting your MCP server to Claude Desktop so you can create memes directly in your conversations.
- Find Your Claude Config: Locate your Claude Desktop configuration file:
- Windows:
%APPDATA%\Claude\claude_desktop_config.json - Mac:
~/Library/Application Support/Claude/claude_desktop_config.json
- Add Your MCP Server: Edit the config file to include your server:
{
"mcpServers": {
"imgflip": {
"command": "python",
"args": ["/full/path/to/your/imgflip_mcp_server.py"]
}
}
}
Replace /full/path/to/your/imgflip_mcp_server.py with the actual path to your Python file.
- Restart Claude Desktop: Close and reopen Claude Desktop for the changes to take effect.
Using Your MCP Server with Claude
Once connected, you can now have conversations like this:
You: “Show me some popular meme templates” Claude: Uses the get_popular_memes tool and shows you a list
You: “Create a meme using the ‘One Does Not Simply’ template with the text ‘One does not simply’ at the top and ‘Build an MCP server in 40 seconds’ at the bottom” Claude: Uses the create_meme tool and provides you with a direct link to your custom meme
How This All Works (The Simple Explanation)
Think of your MCP server as a translator between Claude and the Imgflip API:
- Claude wants to create a meme but doesn’t know how to talk to Imgflip
- Your MCP Server speaks both “Claude language” and “Imgflip language”
- When Claude says “create a meme,” your server translates this into the specific API calls that Imgflip understands
- The server gets the result from Imgflip and translates it back into something Claude can share with you
What You Can Build Next
Now that you understand the basics, you can create MCP servers for almost any API or service:
- Weather Server: Connect to weather APIs for real-time forecasts
- Calendar Server: Integrate with Google Calendar or Outlook
- Database Server: Query your company’s database directly through Claude
- Social Media Server: Post to Twitter, LinkedIn, or Instagram
- File Manager: Let Claude organize, search, or analyze your files
- Email Server: Send emails or check your inbox through conversation We now have everything you need to create our first MCP server. The example we built connects Claude to meme generation, but the same principles apply to any API or service you want to integrate.
If you like this blog you should also check out the videos I make on Instagram: Instagram
In case of any queries, feel free to reach out to me on paras@varnan.tech