A personal example MCP server that lets tools like Cursor or Claude call Midjourney through the community midjourney npm package and a Discord-backed workflow.
This repo is intentionally small, but it is structured to show the parts I care about in MCP engineering work: clear tool boundaries, typed inputs, explicit error handling, lightweight automated tests, and a documented manual smoke test.
This project is unofficial. It is not affiliated with, endorsed by, or maintained by Midjourney or Discord.
It depends on a community package and a Discord-based integration path that may change or break over time. If you publish or use this project, make sure you understand the terms and policies of the services involved.
The server exposes eight tools:
| Tool | Purpose |
|---|---|
imagine |
Generate a new 2x2 Midjourney image grid from a prompt. |
upscale |
Upscale one image from a previously generated grid. |
variation |
Generate a variation of one image from a previous grid. |
action |
Trigger any follow-up action label returned by Midjourney. |
describe |
Reverse-engineer prompts from an image URL. |
info |
Fetch account and usage information. |
list_images |
Show the images stored in the current MCP session. |
settings |
View or switch fast/relax/remix-related settings. |
I built this as a portfolio-grade MCP example rather than a large product. The goal is to demonstrate:
- a clean TypeScript MCP server structure
- tool registration separated from business logic
- thin runtime wiring with isolated config, client, store, and formatter modules
- automated validation around the parts that are practical to test locally
- a real end-to-end workflow that can be manually exercised with live credentials
src/
config.ts Environment validation
main.ts Startup flow
server.ts MCP server creation
midjourney/client.ts Lazy Midjourney client initialization
store/image-store.ts In-memory generated image state
formatters/ MCP text response helpers
tools/ Tool handlers and registration
types/ Narrow internal types
tests/
*.test.ts Lightweight automated tests
assets/
repo-banner.svg README banner art
- An MCP client calls a tool over stdio.
- The server validates inputs with Zod.
- Tool handlers call the shared Midjourney client wrapper.
- Image-producing tools store follow-up metadata in an in-memory session store.
- The tool returns a text result formatted for an MCP client to show clearly.
One important constraint: list_images, upscale, variation, and action all rely on in-memory state. If the MCP process restarts, that image history is gone. That trade-off keeps the example simple and makes the session behavior explicit.
- Node.js 20+
- npm
- A working Midjourney + Discord setup compatible with the community
midjourneypackage - A Discord auth token, server ID, and channel ID for the target Midjourney workflow
Treat the Discord token like any other secret. Do not commit it, paste it into screenshots, or publish it in client config examples.
This server uses the community midjourney package, which works through Discord rather than an official Midjourney API.
At a high level, you need:
- A Discord server and channel where the Midjourney bot is available.
- A Discord auth token compatible with the underlying package.
- The Discord server ID and channel ID for the place where prompts should run.
Typical setup flow:
- Create your own Discord server or use an existing one where the Midjourney bot is present.
- Make sure the target channel is the one you want this MCP server to use for image generation.
- Enable Discord Developer Mode so you can copy the server ID and channel ID.
- Obtain the Discord auth token required by the upstream
midjourneypackage. - Store those three values in local environment variables or a local
.envfile.
This repo intentionally does not include step-by-step token extraction instructions. The exact method can change, and the safest source of truth is the upstream package documentation plus your own understanding of the services involved.
Useful references:
Expected environment variables:
DISCORD_TOKEN=your-discord-auth-token
DISCORD_SERVER_ID=your-server-id
DISCORD_CHANNEL_ID=your-channel-idnpm ci
cp .env.example .envFill in the values in .env, then:
npm run build
npm startFor local development:
npm run devIf you want to publish this repo with a real Midjourney example image in the README, add one of your own generated images under assets/ and reference it here. I removed the synthetic placeholder banner so the repo does not imply that an abstract mock image came from Midjourney.
Example mcpServers entry for a local client:
{
"mcpServers": {
"midjourney": {
"command": "node",
"args": ["/absolute/path/to/midjourney-mcp/dist/index.js"],
"env": {
"DISCORD_TOKEN": "your-token",
"DISCORD_SERVER_ID": "your-server-id",
"DISCORD_CHANNEL_ID": "your-channel-id"
}
}
}
}If you prefer to keep secrets outside the config file, export the environment variables in your shell before starting the client.
Automated checks:
npm test
npm run typecheck
npm run buildThe automated suite includes a real stdio MCP round-trip: it spawns the server as a child process, connects with an MCP client, lists tools, and calls list_images.
Manual end-to-end validation:
- use a live Midjourney/Discord setup
- run
info - run
imagine - confirm
list_imagesincludes the new image - optionally run
upscaleorvariation
The step-by-step smoke test is documented in docs/manual-smoke-test.md.
- This is an unofficial integration path.
- The image store is process-local and intentionally ephemeral.
- The server does not persist image history across restarts.
- Live end-to-end testing depends on valid external credentials and service availability.
- The repo is optimized as a clear example, not as a fully productionized package.
The automated tests focus on logic that is worth validating locally:
- config parsing
- image-store behavior
- response formatting
- representative tool-handler behavior
That keeps the test suite fast while still proving the key design choices.