DEV Community

Nipurn Agarwal
Nipurn Agarwal Subscriber

Posted on

Building Your Own Meme Generator with Model Context Protocol (MCP)

Building Your Own Meme Generator with Model Context Protocol (MCP)

Model Context Protocol (MCP) has revolutionized how AI assistants interact with external tools and services. In this tutorial, I'll show you how to create a fun and practical Meme Generator MCP that works with AI assistants like Claude in Cursor or Cline.

What is MCP?

The Model Context Protocol (MCP) is a standard that allows AI assistants to interact with external tools, services, and data sources. By creating custom MCP servers, you can extend AI capabilities with specialized tools tailored to your needs.

Project Overview

Our Meme Generator MCP will have three main components:

  1. A Next.js web application that displays memes
  2. An MCP server that handles requests from AI assistants
  3. Configuration for AI tools like Cursor or Cline

Setting Up the Next.js Web App

First, let's create a Next.js project:

npx create-next-app@latest meme-generator
cd meme-generator
Enter fullscreen mode Exit fullscreen mode

Now, let's create our main page in app/page.tsx:

"use client";

import { useState, useEffect } from "react";

export default function Home() {
  const [noText, setNoText] = useState("");
  const [yesText, setYesText] = useState("");
  const [error, setError] = useState<string | null>(null);

  const fetchMemeText = async () => {
    setError(null);
    try {
      const response = await fetch("/api/generate-meme", {
        method: "GET",
      });

      if (!response.ok) {
        const errorText = await response.text();
        throw new Error(
          `HTTP error! status: ${response.status}, body: ${errorText}`
        );
      }

      const data = await response.json();
      if (data.receivedText) {
        setNoText(data.receivedText.noText);
        setYesText(data.receivedText.yesText);
      }
    } catch (error) {
      console.error("Error fetching meme text:", error);
      const errorMessage =
        error instanceof Error ? error.message : "An unknown error occurred";
      setError(errorMessage);
    }
  };

  useEffect(() => {
    const intervalId = setInterval(() => {
      fetchMemeText();
    }, 4000);

    return () => clearInterval(intervalId);
  }, []);

  return (
    <div className="flex flex-col items-center justify-center min-h-screen py-2">
      <h1 className="text-6xl font-bold mb-4">Meme Generator</h1>

      {error && <p className="text-red-500">Error: {error}</p>}

      {noText && yesText && (
        <div>
          <div className="flex items-center">
            <img src="/template_no.png" alt="template_no" className="mr-4" />
            <p className="text-6xl font-bold">{noText}</p>
          </div>
          <div className="flex items-center">
            <img src="/template_yes.png" alt="template_yes" className="mr-4" />
            <p className="text-6xl font-bold">{yesText}</p>
          </div>
        </div>
      )}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Next, we'll create an API route to handle meme generation in app/api/generate-meme/route.ts:

import { NextResponse } from "next/server";

let storedNoText: string = "";
let storedYesText: string = "";

export async function POST(request: Request) {
  try {
    const { noText, yesText } = await request.json();
    console.log("Received text in Next.js API:", { noText, yesText });

    storedNoText = noText;
    storedYesText = yesText;

    return NextResponse.json({
      message: "Text received successfully",
      receivedText: { noText, yesText },
    });
  } catch (error) {
    console.error("Error processing request in Next.js API:", error);
    const errorMessage =
      error instanceof Error ? error.message : "An unknown error occurred";
    return NextResponse.json(
      {
        message: "Error processing request",
        error: errorMessage,
      },
      { status: 500 }
    );
  }
}

export async function GET(request: Request) {
  return NextResponse.json({
    message: "Text retrieved successfully",
    receivedText: { noText: storedNoText, yesText: storedYesText },
  });
}
Enter fullscreen mode Exit fullscreen mode

Creating Meme Templates

You'll need two image templates:

  1. template_no.png - An image showing disappointment/rejection
  2. template_yes.png - An image showing approval/happiness

Place these in the public folder of your Next.js app.

Setting Up the MCP Server

Now, let's create our MCP server:

  1. Create a new directory for your MCP server:
mkdir meme-mcp-server
cd meme-mcp-server
Enter fullscreen mode Exit fullscreen mode
  1. Initialize a Node.js project:
npm init -y
Enter fullscreen mode Exit fullscreen mode
  1. Install the MCP SDK:
npm install @modelcontextprotocol/sdk
Enter fullscreen mode Exit fullscreen mode
  1. Create a TypeScript configuration:
npm install typescript --save-dev
npx tsc --init
Enter fullscreen mode Exit fullscreen mode
  1. Create an index.ts file:
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError,
} from "@modelcontextprotocol/sdk/types.js";

class MemeServer {
  private server: Server;

  constructor() {
    this.server = new Server(
      {
        name: "meme-mcp-server",
        version: "0.1.0",
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();

    this.server.onerror = (error) => console.error("[MCP Error]", error);
    process.on("SIGINT", async () => {
      await this.server.close();
      process.exit(0);
    });
  }

  private setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: "generate_meme",
          description:
            "Generates a meme by sending text to the Next.js backend.",
          inputSchema: {
            type: "object",
            properties: {
              noText: {
                type: "string",
                description:
                  "The text to display at the top of the meme (No text).",
              },
              yesText: {
                type: "string",
                description:
                  "The text to display at the bottom of the meme (Yes text).",
              },
            },
            required: ["noText", "yesText"],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === "generate_meme") {
        const { noText, yesText } = request.params.arguments as {
          noText: string;
          yesText: string;
        };

        try {
          const response = await fetch(
            "http://localhost:3000/api/generate-meme",
            {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
              },
              body: JSON.stringify({ noText, yesText }),
            }
          );

          return {
            content: [
              {
                type: "text",
                text: "Meme generated successfully",
              },
            ],
          };
        } catch (error: any) {
          console.error("Error calling Next.js API:", error);
          return {
            content: [
              {
                type: "text",
                text: `Error generating meme: ${error.message}`,
              },
            ],
            isError: true,
          };
        }
      }
      throw new McpError(
        ErrorCode.MethodNotFound,
        `Unknown tool: ${request.params.name}`
      );
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error("Meme MCP server running on stdio");
    console.log("mcp ok!");
  }
}

const server = new MemeServer();
server.run().catch(console.error);
Enter fullscreen mode Exit fullscreen mode
  1. Build your TypeScript code:
npx tsc
Enter fullscreen mode Exit fullscreen mode

Configuring Your AI Assistant

For Cursor

Create or edit ~/.cursor/mcp.json:

{
  "mcpServers": {
    "meme-mcp-server": {
      "command": "node",
      "args": ["path/to/your/meme-mcp-server/index.js"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

For Cline

Create or edit ~/.config/cline/cline_mcp_settings.json:

{
  "mcpServers": {
    "meme-mcp-server": {
      "command": "node",
      "args": ["path/to/your/meme-mcp-server/index.js"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Using Your Meme Generator

  1. Start your Next.js app:
cd meme-generator
npm run dev
Enter fullscreen mode Exit fullscreen mode
  1. Open your browser to http://localhost:3000

  2. In Cursor or Cline, you can now generate memes with commands like:

Use "generate_meme" tool of "meme-mcp-server",
and send "noText": "Coding without AI assistants"
and "yesText": "Coding with AI assistants".
Enter fullscreen mode Exit fullscreen mode

Enhancing Your Meme Generator

Here are some ideas to take your meme generator to the next level:

  1. Add more templates - Create different meme formats by adding more template options
  2. Add AI-generated images - Integrate with image generation APIs to create custom meme backgrounds
  3. Add a gallery - Store generated memes and let users browse previously created ones
  4. Social sharing - Add buttons to share memes directly to social media

Conclusion

Building custom MCP tools lets you extend AI assistants with specialized capabilities. This meme generator is just the beginning - you can create MCP servers for virtually any task, from data analysis to workflow automation.

By mastering MCP development, you can create powerful, custom AI-powered tools that perfectly fit your specific needs and workflows.

What will you build next?

Top comments (0)