@thenavidm/midjourney-mcp-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +677 -0
  3. package/SKILL.md +184 -0
  4. package/dist/api/client.d.ts +72 -0
  5. package/dist/api/client.js +278 -0
  6. package/dist/api/client.js.map +1 -0
  7. package/dist/api/download.d.ts +41 -0
  8. package/dist/api/download.js +108 -0
  9. package/dist/api/download.js.map +1 -0
  10. package/dist/api/errors.d.ts +75 -0
  11. package/dist/api/errors.js +166 -0
  12. package/dist/api/errors.js.map +1 -0
  13. package/dist/api/jobs.d.ts +140 -0
  14. package/dist/api/jobs.js +296 -0
  15. package/dist/api/jobs.js.map +1 -0
  16. package/dist/api/moodboards.d.ts +88 -0
  17. package/dist/api/moodboards.js +189 -0
  18. package/dist/api/moodboards.js.map +1 -0
  19. package/dist/capture.d.ts +27 -0
  20. package/dist/capture.js +162 -0
  21. package/dist/capture.js.map +1 -0
  22. package/dist/cli.d.ts +92 -0
  23. package/dist/cli.js +633 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/config.d.ts +37 -0
  26. package/dist/config.js +90 -0
  27. package/dist/config.js.map +1 -0
  28. package/dist/content/prompt.d.ts +69 -0
  29. package/dist/content/prompt.js +173 -0
  30. package/dist/content/prompt.js.map +1 -0
  31. package/dist/doctor.d.ts +19 -0
  32. package/dist/doctor.js +161 -0
  33. package/dist/doctor.js.map +1 -0
  34. package/dist/format/jobs.d.ts +71 -0
  35. package/dist/format/jobs.js +211 -0
  36. package/dist/format/jobs.js.map +1 -0
  37. package/dist/index.d.ts +13 -0
  38. package/dist/index.js +161 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/safety.d.ts +52 -0
  41. package/dist/safety.js +100 -0
  42. package/dist/safety.js.map +1 -0
  43. package/dist/server.d.ts +10 -0
  44. package/dist/server.js +57 -0
  45. package/dist/server.js.map +1 -0
  46. package/dist/tools/create.d.ts +96 -0
  47. package/dist/tools/create.js +375 -0
  48. package/dist/tools/create.js.map +1 -0
  49. package/dist/tools/download.d.ts +17 -0
  50. package/dist/tools/download.js +47 -0
  51. package/dist/tools/download.js.map +1 -0
  52. package/dist/tools/explore.d.ts +8 -0
  53. package/dist/tools/explore.js +57 -0
  54. package/dist/tools/explore.js.map +1 -0
  55. package/dist/tools/index.d.ts +3 -0
  56. package/dist/tools/index.js +16 -0
  57. package/dist/tools/index.js.map +1 -0
  58. package/dist/tools/jobs.d.ts +18 -0
  59. package/dist/tools/jobs.js +92 -0
  60. package/dist/tools/jobs.js.map +1 -0
  61. package/dist/tools/kit.d.ts +84 -0
  62. package/dist/tools/kit.js +104 -0
  63. package/dist/tools/kit.js.map +1 -0
  64. package/dist/tools/library.d.ts +22 -0
  65. package/dist/tools/library.js +185 -0
  66. package/dist/tools/library.js.map +1 -0
  67. package/dist/tools/profile.d.ts +2 -0
  68. package/dist/tools/profile.js +89 -0
  69. package/dist/tools/profile.js.map +1 -0
  70. package/dist/transport/cdp.d.ts +217 -0
  71. package/dist/transport/cdp.js +607 -0
  72. package/dist/transport/cdp.js.map +1 -0
  73. package/dist/transport/http.d.ts +18 -0
  74. package/dist/transport/http.js +48 -0
  75. package/dist/transport/http.js.map +1 -0
  76. package/package.json +72 -0
package/README.md ADDED
@@ -0,0 +1,677 @@
1
+ <div align="center">
2
+ <img src="https://cdn.navid.media/connectors/midjourney-icon.png" alt="Midjourney" width="88">
3
+ </div>
4
+
5
+ # Midjourney MCP + CLI
6
+
7
+ [![Licence](https://img.shields.io/badge/licence-MIT-green)](./LICENSE)
8
+ [![YouTube](https://img.shields.io/badge/YouTube-@thenavidm-red?logo=youtube&logoColor=white)](https://youtube.com/@thenavidm?sub_confirmation=1)
9
+ [![X](https://img.shields.io/badge/X-@thenavidm-black?logo=x)](https://x.com/thenavidm)
10
+
11
+ Midjourney MCP server and CLI for Claude Code and AI agents. 27 tools for generating images, following jobs to completion, downloading the real files, and building moodboards that make a style reusable.
12
+
13
+ Midjourney publishes no API, so this drives a real Chrome that is signed in as you.
14
+
15
+ There is no key to paste and no cookie to export. You sign in once, in a window, and the session lives in a browser profile rather than in a config file.
16
+
17
+ 27 tools, on both surfaces. It waits for jobs to finish and hands back the actual files, not a screenshot of them.
18
+
19
+ Built and maintained by [Navid Moazzez](https://navid.me).
20
+
21
+ ```
22
+ You: make a moodboard for cold Nordic product shots, fill it, then shoot a jar of face cream in that style
23
+
24
+ Claude: Built the board and used it.
25
+
26
+ moodboard Nordic Skincare | Still Life 4 images
27
+ job f44b0a9d-e184-4e18-811f-a1ef9482d286
28
+ prompt a ceramic jar of face cream, lid beside it
29
+ style from the moodboard, not the prompt
30
+
31
+ ~/Downloads/midjourney/f44b0a9d-0.png 1.8 MB 960x1200
32
+ ```
33
+
34
+ ## Two ways to use it
35
+
36
+ ### Command line
37
+
38
+ `midjourney-cli` in your terminal, for scripting, cron, pipes, or a quick
39
+ question without opening anything:
40
+
41
+ ```bash
42
+ midjourney-cli # every command, one line each
43
+ midjourney-cli login # sign in once, in a window
44
+ midjourney-cli imagine "a red fox in snow" --ar 16:9 --confirm
45
+ midjourney-cli list-jobs --limit 5 --select id,prompt --json
46
+ midjourney-cli download-job <job-id> --out-dir ./renders
47
+ midjourney-cli list-moodboards --json | jq -r '.moodboards[].title'
48
+ midjourney-cli which "save my pictures to disk" # find the right command
49
+ midjourney-cli <command> --help # what any command takes
50
+ ```
51
+
52
+ `--confirm` is the shell spelling of the confirmation that generating requires.
53
+ `--json` gives JSON, `--compact` puts it on one line, `--select id,status` keeps
54
+ only the fields you name, and errors are JSON on stderr whichever you pick.
55
+
56
+ Handlers return data rather than pre-rendered text, so `--json` gives real
57
+ fields on every command and `jq` works the same way everywhere.
58
+
59
+ ### MCP server, for AI agents
60
+
61
+ `midjourney-mcp` is what Claude Code, Claude Desktop, Cursor and the rest
62
+ launch. You never run it by hand:
63
+
64
+ ```bash
65
+ claude mcp add midjourney -- npx -y @thenavidm/midjourney-mcp-cli@latest
66
+ ```
67
+
68
+ There is nothing to put in `-e`. Run `midjourney-cli login` first.
69
+
70
+ Then just ask: _"shoot that campaign in the style of my High Fashion moodboard"._
71
+
72
+ Every other client is in [section 3](#3-install).
73
+
74
+ ### Which one
75
+
76
+ | What you are doing | Use |
77
+ |---|---|
78
+ | Inside a conversation with an agent | MCP |
79
+ | On claude.ai or your phone | Neither. The browser is on your machine, so a cloud connector cannot reach it |
80
+ | Piping, scripting, cron, CI | CLI |
81
+ | A one-off question in a terminal | CLI |
82
+
83
+ They are the same program reading the same tool definitions, so anything one
84
+ can do, the other can.
85
+
86
+ ## Features
87
+
88
+ Every tool is both a command and an MCP tool, with the same name. The command is
89
+ the tool name with dashes.
90
+
91
+ | Capability | CLI command | MCP tool |
92
+ |---|---|---|
93
+ | Who am I, is the session live | `midjourney-cli whoami` | `whoami` |
94
+ | Generate and wait for the images | `midjourney-cli imagine` | `imagine` |
95
+ | Generate without waiting | `midjourney-cli submit-imagine` | `submit_imagine` |
96
+ | Re-run a job, or re-render at HD | `midjourney-cli rerun-job` | `rerun_job` |
97
+ | Vary one image from a grid | `midjourney-cli vary-image` | `vary_image` |
98
+ | Recent generations | `midjourney-cli list-jobs` | `list_jobs` |
99
+ | One job by id | `midjourney-cli get-job` | `get_job` |
100
+ | Wait for a job to finish | `midjourney-cli wait-for-job` | `wait_for_job` |
101
+ | What is rendering now | `midjourney-cli get-queue` | `get_queue` |
102
+ | Save the real files to disk | `midjourney-cli download-job` / `download-url` | `download_job` / `download_url` |
103
+ | List and read moodboards | `midjourney-cli list-moodboards` / `get-moodboard` | `list_moodboards` / `get_moodboard` |
104
+ | Create a moodboard | `midjourney-cli create-moodboard` | `create_moodboard` |
105
+ | Add to, remove from a moodboard | `midjourney-cli add-to-moodboard` / `remove-from-moodboard` | `add_to_moodboard` / `remove_from_moodboard` |
106
+ | Personalisation profiles | `midjourney-cli list-personalized-profiles` | `list_personalized_profiles` |
107
+ | Folders and storage | `midjourney-cli list-folders` / `get-storage` | `list_folders` / `get_storage` |
108
+ | The public explore feed | `midjourney-cli explore-feed` | `explore_feed` |
109
+ | Any endpoint with no named tool | `midjourney-cli api-get` / `submit-raw-job` | `api_get` / `submit_raw_job` |
110
+ | Check your setup | `midjourney-cli doctor` | not a tool |
111
+ | Sign in | `midjourney-cli login` | not a tool |
112
+ | Record what the web app calls | `midjourney-cli capture` | not a tool |
113
+ | Find the right command | `midjourney-cli which "..."` | not a tool |
114
+
115
+ All 27 with their arguments are in [section 6](#6-tools).
116
+
117
+ ## Contents
118
+
119
+ | | Section | |
120
+ |---|---|---|
121
+ | 1 | [What you can ask it](#1-what-you-can-ask-it) | Real prompts, not features |
122
+ | 2 | [Sign in once](#2-sign-in-once) | No key, no cookie |
123
+ | 3 | [Install](#3-install) | Every client, copy and paste, plus the shell |
124
+ | 4 | [Output and exit codes](#4-output-and-exit-codes) | What scripts branch on |
125
+ | 5 | [Which surface, and what each costs](#5-which-surface-and-what-each-costs) | ~8,900 tokens a turn, or nothing |
126
+ | 6 | [Tools](#6-tools) | All 27, by what they reach |
127
+ | 7 | [Spending safely](#7-spending-safely) | Why generating asks twice |
128
+ | 8 | [Prompts and parameters](#8-prompts-and-parameters) | The grammar, validated before you pay |
129
+ | 9 | [Moodboards](#9-moodboards) | Turning a look into something reusable |
130
+ | 10 | [How it works](#10-how-it-works) | Architecture, and why a browser |
131
+ | 11 | [Your data](#11-your-data) | What is stored and where |
132
+ | 12 | [Risks](#12-risks) | Read this before you install |
133
+ | 13 | [Troubleshooting](#13-troubleshooting) | When something breaks |
134
+ | | [Environment variables](#environment-variables) | Every knob, and its default |
135
+ | 14 | [FAQ](#14-faq) | Including what an MCP server is |
136
+
137
+ ## 1. What you can ask it
138
+
139
+ - Make me a 16:9 hero image of a red fox asleep in snow, muted palette
140
+ - Shoot that campaign in the style of my High Fashion moodboard
141
+ - Make a moodboard for cold Nordic product shots, fill it, then shoot a jar of face cream in that style
142
+ - Generate four logo concepts at low stylize so they stay literal, and save them
143
+ - Vary the second one, strong, and save the results
144
+ - Take that last image's seed and try it again with chaos 40
145
+ - What is in my Midjourney queue right now?
146
+ - Download everything I generated today into ./renders
147
+ - Show me my last ten jobs with just the prompt and the image URLs
148
+ - Re-run job 3f9c1a2b with the prompt changed to say "at dusk"
149
+
150
+ The thing you cannot do without this: hand an agent a brief and get finished
151
+ image files back. Every other route stops at a job id, or at a screenshot of the
152
+ image rather than the image. This waits for the render and writes the real bytes
153
+ to disk, so the next step in a pipeline has something to open.
154
+
155
+ ## 2. Sign in once
156
+
157
+ There is no API key. Midjourney does not issue one, and this server never
158
+ handles a credential of any kind.
159
+
160
+ Instead it runs Chrome against a profile of its own, at
161
+ `~/.midjourney-mcp/chrome-profile`. You sign in there once and the session
162
+ persists, exactly as it would in a browser you use by hand.
163
+
164
+ npx -y @thenavidm/midjourney-mcp-cli@latest login
165
+
166
+ A Chrome window opens on midjourney.com. Sign in. The command waits, notices,
167
+ and exits.
168
+
169
+ The profile is separate from your everyday Chrome on purpose. Nothing here can
170
+ see your normal browsing, your other logins, or your history, and your normal
171
+ browser does not need to be running.
172
+
173
+ To revoke it, sign out in that window, or delete the profile:
174
+
175
+ rm -rf ~/.midjourney-mcp/chrome-profile
176
+
177
+ ## 3. Install
178
+
179
+ Node 22 or newer, and Google Chrome. Nothing else.
180
+
181
+ npx -y @thenavidm/midjourney-mcp-cli --version
182
+
183
+ > [!NOTE]
184
+ > Not published to npm yet. Until it is, clone the repo and run `npm install && npm run build`, then use `node dist/index.js` wherever this page says `npx -y @thenavidm/midjourney-mcp-cli@latest`.
185
+
186
+ Node 22 is the floor because the browser connection uses the global `WebSocket`
187
+ that landed in that release. That is also why it has no dependency doing it.
188
+
189
+ ### Claude Code
190
+
191
+ ```bash
192
+ claude mcp add midjourney -- npx -y @thenavidm/midjourney-mcp-cli@latest
193
+ ```
194
+
195
+ `--scope user` makes it available in every project rather than the current one.
196
+
197
+ ### Claude Desktop
198
+
199
+ | Platform | Path |
200
+ |---|---|
201
+ | macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
202
+ | Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
203
+
204
+ ```json
205
+ {
206
+ "mcpServers": {
207
+ "midjourney": {
208
+ "command": "npx",
209
+ "args": ["-y", "@thenavidm/midjourney-mcp-cli@latest"]
210
+ }
211
+ }
212
+ }
213
+ ```
214
+
215
+ There is also a one-click `.mcpb` bundle on the release page, installed through
216
+ **Settings, Extensions, Install Extension**.
217
+
218
+ > [!TIP]
219
+ > Claude Desktop does not inherit your shell PATH, so a bare `npx` can fail silently. Use the absolute path from `which npx`, and fully quit the app rather than closing the window.
220
+
221
+ ### Cursor
222
+
223
+ `.cursor/mcp.json`, the same JSON shape as Claude Desktop, key `mcpServers`.
224
+
225
+ ### VS Code
226
+
227
+ `.vscode/mcp.json`. The key is `servers`, not `mcpServers`, and the entry takes
228
+ `"type": "stdio"`.
229
+
230
+ ```json
231
+ {
232
+ "servers": {
233
+ "midjourney": {
234
+ "type": "stdio",
235
+ "command": "npx",
236
+ "args": ["-y", "@thenavidm/midjourney-mcp-cli@latest"]
237
+ }
238
+ }
239
+ }
240
+ ```
241
+
242
+ ### Codex CLI
243
+
244
+ `~/.codex/config.toml`:
245
+
246
+ ```toml
247
+ [mcp_servers.midjourney]
248
+ command = "npx"
249
+ args = ["-y", "@thenavidm/midjourney-mcp-cli@latest"]
250
+ ```
251
+
252
+ ### The shell
253
+
254
+ Both binaries come from the same install. `midjourney-cli` with no arguments
255
+ lists every command.
256
+
257
+ ### Check it worked
258
+
259
+ npx -y @thenavidm/midjourney-mcp-cli@latest doctor
260
+
261
+ It checks in dependency order and stops at the first real problem, because these
262
+ failures all produce the same symptom from a tool call and need completely
263
+ different fixes.
264
+
265
+ The two that actually happen:
266
+
267
+ **`browser running: FAIL`.** Chrome is not up on the DevTools port. It starts on
268
+ demand on the first tool call, so this is only a problem if you have set
269
+ `MIDJOURNEY_CHROME_LAUNCH=0`. Run `login` to start it by hand.
270
+
271
+ **`signed in: FAIL`.** The window is open but the profile is signed out. Run
272
+ `login` again.
273
+
274
+ ## 4. Output and exit codes
275
+
276
+ Results on stdout, errors on stderr as JSON, so one parse handles both.
277
+
278
+ | Flag | Result |
279
+ |---|---|
280
+ | none | pretty JSON |
281
+ | `--json` | JSON, always |
282
+ | `--compact` | the same JSON on one line |
283
+ | `--select a,b.c` | keep only these fields. Dotted paths descend, arrays are traversed element-wise |
284
+ | `--agent` | compact JSON. Never implies `--confirm` |
285
+
286
+ `--select` matters more here than it looks. One explore page is tens of
287
+ kilobytes, most of it layout metadata, and an agent piping that into its context
288
+ pays for every byte.
289
+
290
+ `--agent` deliberately does **not** imply confirmation, unlike the equivalent in
291
+ some other CLIs. A flag an agent passes by habit must never be the thing that
292
+ authorises a charge.
293
+
294
+ | Code | Means |
295
+ |---|---|
296
+ | `0` | it worked |
297
+ | `1` | it failed: signed out, a refused write, an API error |
298
+ | `2` | it was typed wrong: a missing flag, a bad value, a bad `--ar` |
299
+
300
+ ## 5. Which surface, and what each costs
301
+
302
+ An MCP server is expensive and a CLI is free.
303
+
304
+ The `tools/list` payload for these 27 tools is about **8,900 tokens**, plus the
305
+ server instructions. That is charged on every turn of every conversation, used
306
+ or not, because the descriptions are long and carry the parameter grammar.
307
+
308
+ A CLI costs nothing until it is called. The skill mentions it in one line, and
309
+ the model pays only when it runs something.
310
+
311
+ So the two are not competing:
312
+
313
+ | Where the work happens | Surface |
314
+ |---|---|
315
+ | Inside a conversation with an agent | MCP |
316
+ | Piping, scripting, cron, CI | CLI |
317
+ | A one-off question in a terminal | CLI |
318
+
319
+ ## 6. Tools
320
+
321
+ ### Making images
322
+
323
+ | Tool | What it does |
324
+ |---|---|
325
+ | `imagine` | Generate, wait for the job, return the images. Optionally save them. Spends |
326
+ | `submit_imagine` | Submit and return the job id without waiting. Spends |
327
+ | `rerun_job` | Run an existing job again, optionally with new wording or at HD. Spends |
328
+ | `vary_image` | Four variations of one image from a grid, subtle or strong. Spends |
329
+ | `submit_raw_job` | Send a job type this server does not model yet. Spends |
330
+
331
+ ### Following work
332
+
333
+ | Tool | What it does |
334
+ |---|---|
335
+ | `list_jobs` | Recent generations, newest first, with status and image URLs |
336
+ | `get_job` | One job by id, with its real status |
337
+ | `wait_for_job` | Block until a job finishes, fails or is moderated |
338
+ | `get_queue` | What is running now, and how much concurrency the plan allows |
339
+ | `job_updates` | The live delta feed the web app itself polls |
340
+
341
+ ### Getting the files
342
+
343
+ | Tool | What it does |
344
+ |---|---|
345
+ | `download_job` | Write a job's images to disk. Real files, full resolution |
346
+ | `download_url` | Write one asset to disk by URL |
347
+
348
+ ### Moodboards and style
349
+
350
+ | Tool | What it does |
351
+ |---|---|
352
+ | `list_moodboards` | Every board, with how many reference images each holds |
353
+ | `get_moodboard` | One board by name, and the references a generation would use |
354
+ | `create_moodboard` | Start a new board for a look |
355
+ | `add_to_moodboard` | Put a job's renders, or any URLs, onto a board |
356
+ | `remove_from_moodboard` | Take images off a board. Needs `confirm` |
357
+ | `list_personalized_profiles` | Profiles, with how many images each was trained on |
358
+
359
+ ### Your account
360
+
361
+ | Tool | What it does |
362
+ |---|---|
363
+ | `whoami` | Which account is signed in, and whether the browser is reachable |
364
+ | `list_folders` | Folders in the Organise view |
365
+ | `get_storage` | Storage used against what the plan allows |
366
+ | `list_following` | Creators this account follows |
367
+ | `list_model_ratings` | Pending rating tasks, which earn fast hours |
368
+ | `get_contest_ranking_count` | Contest rounds completed |
369
+
370
+ ### Explore, and the escape hatch
371
+
372
+ | Tool | What it does |
373
+ |---|---|
374
+ | `explore_feed` | The public feed, with prompts and image URLs |
375
+ | `explore_style_likes` | Which styles this account has liked |
376
+ | `api_get` | Any `/api/` path, for endpoints with no named tool yet |
377
+
378
+ `midjourney-cli which "<what you want>"` resolves a capability described in
379
+ words to the command that does it, so you do not have to read this table.
380
+
381
+ ## 7. Spending safely
382
+
383
+ Reads work freely. What is guarded is spending.
384
+
385
+ Every generation burns GPU time from a paid plan and there are no refunds, so
386
+ `imagine`, `submit_imagine`, `rerun_job`, `vary_image` and `submit_raw_job` take
387
+ `confirm: true`, or `--confirm` at the terminal.
388
+
389
+ Nothing reversible asks. Adding to a moodboard does not, because
390
+ `remove_from_moodboard` undoes it, and confirming reversible things is how a
391
+ model learns to pass `confirm` by reflex, which defeats the gate on spending.
392
+
393
+ A generation is not annotated destructive, because it destroys nothing. It has
394
+ its own risk level, so a client deciding what to auto-approve is told the truth
395
+ about what it is approving.
396
+
397
+ ```
398
+ MIDJOURNEY_READ_ONLY=1 removes every tool that is not a read, 17 remain
399
+ MIDJOURNEY_ALLOW_DESTRUCTIVE=0 keeps reads and downloads, blocks anything that spends
400
+ MIDJOURNEY_AUDIT_LOG=<path> one JSON line per attempted change, allowed and blocked
401
+ ```
402
+
403
+ ## 8. Prompts and parameters
404
+
405
+ Write the subject in `prompt` and everything else as named arguments. Do not put
406
+ `--ar` inside the prompt string.
407
+
408
+ The arguments are validated before anything is spent. Midjourney is not: it
409
+ silently ignores or clamps most malformed parameters rather than reporting them,
410
+ so a typo costs a generation and comes back looking like a bad result rather
411
+ than a mistake.
412
+
413
+ | Argument | What it does |
414
+ |---|---|
415
+ | `aspect` | `"16:9"`, `"3:2"`, `"1:1"`. Sent as `--ar` |
416
+ | `stylize` | 0-1000. Low follows the prompt, high looks prettier and drifts |
417
+ | `chaos` | 0-100. How different the four results are from each other |
418
+ | `seed` | Reuse with an identical prompt to iterate on one image |
419
+ | `style_refs` | An image URL, a numeric code, or `random`. Sent as `--sref` |
420
+ | `omni_refs` | Carry a character or object across images. The v7+ replacement for `--cref` |
421
+ | `image_prompts` | Direct image URLs, including `s.mj.run` links, used as visual input |
422
+ | `negative` | Things to keep out. Sent as `--no` |
423
+ | `raw` | Less automatic prettification. Good for photographic work |
424
+ | `draft` | Much faster and cheaper, lower fidelity. Good for exploring |
425
+ | `speed` | `fast`, `relax` or `turbo` |
426
+
427
+ At the terminal, Midjourney's own spellings work as aliases: `--ar`, `--sref`,
428
+ `--oref`, `--iw`, `--sw`, `--ow`, `--q`, `--no`, `--v`.
429
+
430
+ ## 9. Moodboards
431
+
432
+ A moodboard is a curated pile of reference images. Naming one is far more
433
+ reliable than describing a look in words, because the board *is* the look.
434
+
435
+ The loop:
436
+
437
+ ```bash
438
+ midjourney-cli create-moodboard "Nordic Skincare | Still Life"
439
+ midjourney-cli imagine "<a long, specific style description>" --confirm
440
+ midjourney-cli add-to-moodboard "Nordic Skincare" --job-id <job>
441
+ midjourney-cli imagine "a ceramic jar of face cream, lid beside it" \
442
+ --moodboard "Nordic Skincare" --sw 400 --confirm
443
+ ```
444
+
445
+ After the third line the style is a name, and a nine-word prompt reproduces it.
446
+
447
+ Partial names work: `"High Fashion"` finds `"High Fashion | Woman"`. An ambiguous
448
+ name errors with the candidates rather than guessing, because picking the wrong
449
+ board costs a generation to discover. References are sampled across the board
450
+ rather than taken from the front, so a 242-image board does not always draw on
451
+ its oldest images.
452
+
453
+ `profile` does something different: it biases toward images the account has
454
+ rated, rather than toward a set of pictures.
455
+
456
+ ## 10. How it works
457
+
458
+ Midjourney publishes no API. The endpoints under `/api/` are the ones its own
459
+ web app calls, and they sit behind a Cloudflare interstitial that answers a plain
460
+ client with a 403 challenge page rather than JSON.
461
+
462
+ That challenge is not defeated by a header. The `cf_clearance` cookie is bound to
463
+ the IP, the User-Agent and the TLS fingerprint together, so a cookie lifted out
464
+ of a browser and replayed from Node is a different client and gets stopped.
465
+
466
+ So rather than impersonate a browser, this drives one. Requests are issued by
467
+ `fetch()` running inside a real midjourney.com page, in a real Chrome that is
468
+ really signed in. Same origin, same cookies, same fingerprint, same IP,
469
+ credentials attached by the browser itself. There is nothing to spoof because
470
+ nothing is being faked.
471
+
472
+ Chrome 136 stopped honouring `--remote-debugging-port` on the default profile, so
473
+ this owns a profile instead: a dedicated `user-data-dir` you sign into once.
474
+
475
+ Both surfaces are generated from one `ALL_TOOLS` array. `register()` turns a spec
476
+ into an MCP tool and `cli.ts` turns the same spec into a shell command, through
477
+ the same handler and the same write guard, so a tool added tomorrow is a command
478
+ tomorrow and the two cannot drift. A test asserts that.
479
+
480
+ Downloads are read with an in-page `fetch`, which needs no new tab and no visible
481
+ activity. The CDN sends `access-control-allow-origin: *`, so the bytes come back
482
+ exactly as served.
483
+
484
+ ## 11. Your data
485
+
486
+ Nothing leaves your machine except the requests to Midjourney that you asked for.
487
+ There is no telemetry, no analytics and no backend.
488
+
489
+ The session lives in a Chrome profile on your own disk. This process never reads
490
+ a cookie, stores a token, or sees a password.
491
+
492
+ Downloads go where you point them, `~/Downloads/midjourney` by default. The audit
493
+ log, when enabled, is a local file.
494
+
495
+ ## 12. Risks
496
+
497
+ **This is unofficial, and Midjourney's terms do not permit automated access.**
498
+ Every unofficial client carries a risk to the account, this one included. It
499
+ moves at human pace and acts through a real browser session rather than
500
+ imitating one, which is the honest limit of what any tool here can do about
501
+ that.
502
+
503
+ **It spends money.** A loop over twenty prompt ideas is twenty charges. Use
504
+ `MIDJOURNEY_READ_ONLY=1` when pointing an unattended agent at the account, and
505
+ `MIDJOURNEY_AUDIT_LOG` when you want a record.
506
+
507
+ **The endpoints are undocumented and can change without notice.** Job records are
508
+ parsed defensively and partial answers are preferred to failures, but a large
509
+ enough upstream change will still break something.
510
+
511
+ ## 13. Troubleshooting
512
+
513
+ Start with `doctor`. It orders the checks so the first failure is the one to fix.
514
+
515
+ | Symptom | Cause |
516
+ |---|---|
517
+ | `Cloudflare served a challenge` | The interstitial has not been cleared in that profile. Open the window and let it finish once |
518
+ | `The browser profile is not signed in` | Signed out, or the session expired. Run `login` |
519
+ | `No Chrome or Chromium found` | Chrome is not where it is normally looked for. Set `MIDJOURNEY_CHROME_PATH` |
520
+ | `DevTools never answered` | Another Chrome is using that profile directory. Quit it, or set `MIDJOURNEY_CHROME_PROFILE` elsewhere |
521
+ | `Midjourney refused ... for billing reasons` | Out of fast hours, or the subscription lapsed. Switch to `speed: "relax"` |
522
+ | Job accepted, then never appears | The account is at its concurrent-job limit. Check `get_queue` |
523
+ | `had not finished after 600s` | Normal on relax mode. The job is still running; raise `MIDJOURNEY_JOB_TIMEOUT_MS` |
524
+ | Every command times out at once | A native dialog was left open in the window. Dialogs are auto-dismissed now; if it persists, close the tab |
525
+ | Downloads are empty or fail | The asset URL expired. Re-read the job with `get_job` for fresh URLs |
526
+
527
+ ## Environment variables
528
+
529
+ Every one of these is optional. The defaults are what you want unless you are doing something unusual.
530
+
531
+ | Variable | Default | What it does |
532
+ |---|---|---|
533
+ | `MIDJOURNEY_CHROME_PROFILE` | `~/.midjourney-mcp/chrome-profile` | The browser profile holding the session |
534
+ | `MIDJOURNEY_CHROME_PATH` | found automatically | The Chrome binary |
535
+ | `MIDJOURNEY_CHROME_LAUNCH` | `1` | Start Chrome on demand. `0` only attaches to a running one |
536
+ | `MIDJOURNEY_CDP_URL` | `http://127.0.0.1:9222` | Where DevTools listens |
537
+ | `MIDJOURNEY_HEADLESS` | `0` | Run without a window. Sign in first, a window is needed for that |
538
+ | `MIDJOURNEY_ORIGIN` | `https://www.midjourney.com` | The site being driven |
539
+ | `MIDJOURNEY_USER_ID` | discovered | Skip user-id discovery |
540
+ | `MIDJOURNEY_DEFAULT_SPEED` | `fast` | `fast`, `relax` or `turbo` |
541
+ | `MIDJOURNEY_DEFAULT_VERSION` | `7` | Model version appended as `--v` |
542
+ | `MIDJOURNEY_DOWNLOAD_DIR` | `~/Downloads/midjourney` | Where downloads land |
543
+ | `MIDJOURNEY_REQUEST_TIMEOUT_MS` | `30000` | Per-request deadline |
544
+ | `MIDJOURNEY_MIN_REQUEST_INTERVAL_MS` | `700` | Floor between requests, jittered |
545
+ | `MIDJOURNEY_MAX_RETRIES` | `3` | Retries on 429 and 5xx |
546
+ | `MIDJOURNEY_JOB_TIMEOUT_MS` | `600000` | How long to wait for a job |
547
+ | `MIDJOURNEY_JOB_POLL_INTERVAL_MS` | `3000` | First poll interval, widening from there |
548
+ | `MIDJOURNEY_REFRESH_VIEW` | `1` | Reload the open window after a generation so it shows the new work |
549
+ | `MIDJOURNEY_READ_ONLY` | `0` | Hide everything that is not a read |
550
+ | `MIDJOURNEY_ALLOW_DESTRUCTIVE` | `1` | `0` blocks anything that spends |
551
+ | `MIDJOURNEY_AUDIT_LOG` | unset | Append-only log of every attempted change |
552
+ | `MIDJOURNEY_HTTP_PORT` | `8787` | Port for `--http` |
553
+ | `MIDJOURNEY_HTTP_HOST` | `127.0.0.1` | Interface for `--http` |
554
+ | `MIDJOURNEY_HTTP_TOKEN` | unset | Bearer token. Required to listen off loopback |
555
+
556
+ ## 14. FAQ
557
+
558
+ <details>
559
+ <summary><b>What is an MCP server?</b></summary>
560
+
561
+ An MCP server is a standard way to give an AI assistant real access to a tool, so it can act rather than guess. You install it once, your assistant gains the tools, and it works in Claude, Cursor, ChatGPT and anything else speaking MCP.
562
+
563
+ </details>
564
+
565
+ <details>
566
+ <summary><b>What is Midjourney?</b></summary>
567
+
568
+ Midjourney is an image generation service. You write a prompt, it renders four images, and you refine from there. It runs on the web at midjourney.com and in Discord, on a paid subscription.
569
+
570
+ </details>
571
+
572
+ <details>
573
+ <summary><b>Does Midjourney have an API?</b></summary>
574
+
575
+ Midjourney has no public API and has never shipped one. Every "Midjourney API" on sale is an unofficial wrapper around the same web endpoints this server uses, usually running on somebody else's account. This one at least runs on yours, in your browser, on your machine.
576
+
577
+ </details>
578
+
579
+ <details>
580
+ <summary><b>Is this against Midjourney's terms?</b></summary>
581
+
582
+ Midjourney's terms do not permit automated access, so yes, and there is no way to build this that does not. Any tool of this kind carries a risk to the account. Decide whether that trade is worth it before installing, and know that no unofficial client can promise otherwise.
583
+
584
+ </details>
585
+
586
+ <details>
587
+ <summary><b>Do I need to be technical?</b></summary>
588
+
589
+ You need to be comfortable pasting one command into a terminal and signing in to a website. There is no key to generate, no dashboard to navigate, and no config file to edit by hand.
590
+
591
+ </details>
592
+
593
+ <details>
594
+ <summary><b>Is my data sent anywhere?</b></summary>
595
+
596
+ Nothing leaves your machine except the requests to Midjourney that you asked for. The server has no telemetry, no analytics and no backend. Your session lives in a Chrome profile on your own disk and this process never reads it.
597
+
598
+ </details>
599
+
600
+ <details>
601
+ <summary><b>Can it spend money without me noticing?</b></summary>
602
+
603
+ It refuses to generate anything without an explicit confirmation on every call, and it records what it attempted when you set `MIDJOURNEY_AUDIT_LOG`. Set `MIDJOURNEY_READ_ONLY=1` and the generating tools disappear from the list entirely, which is the setting to use when pointing an unattended agent at the account.
604
+
605
+ </details>
606
+
607
+ <details>
608
+ <summary><b>What can it do that the website cannot?</b></summary>
609
+
610
+ It puts generation into a pipeline. An agent can take a brief, build a validated prompt, wait for the render, download the files and hand them to the next step, without a person clicking through four screens. It also refuses malformed parameters before they cost you a generation, which the website does not.
611
+
612
+ </details>
613
+
614
+ <details>
615
+ <summary><b>Does it work with ChatGPT and Cursor?</b></summary>
616
+
617
+ It works with Cursor, VS Code, Codex CLI, Windsurf and anything else that runs a local MCP server over stdio. claude.ai on the web runs connectors from Anthropic's cloud, so it cannot reach a browser on your machine and this is not usable there.
618
+
619
+ </details>
620
+
621
+ <details>
622
+ <summary><b>Can I run it without a visible browser window?</b></summary>
623
+
624
+ You can set `MIDJOURNEY_HEADLESS=1` once the profile is signed in, though signing in needs a window, so do that first. Expect Cloudflare to be less forgiving of a headless session than a visible one.
625
+
626
+ </details>
627
+
628
+ <details>
629
+ <summary><b>Why does it need Node 22?</b></summary>
630
+
631
+ The browser connection uses the global `WebSocket` that became stable in Node 22. Relying on it means the part of this server that matters most has no dependencies at all.
632
+
633
+ </details>
634
+
635
+ <details>
636
+ <summary><b>How do I disconnect it?</b></summary>
637
+
638
+ Remove the entry from your client's config, then delete `~/.midjourney-mcp/chrome-profile` to drop the session. Nothing else is left behind.
639
+
640
+ </details>
641
+
642
+ ## Questions
643
+
644
+ Run into a problem or have a question? [Open an issue](https://github.com/navidmoazzez/midjourney-mcp-cli/issues) and I will help.
645
+
646
+ ## About the author 👋
647
+
648
+ Navid Moazzez is a leading AI business strategist, and the host of the AI Creator Summit, watched by 100,000+ creators. He helps creators and founders master AI and build their own AI Operating System (AI OS) to automate their business and life. This Midjourney MCP server is one piece of that system.
649
+
650
+ **Links**
651
+
652
+ - Personal website: [navid.me](https://navid.me)
653
+ - Store: [navid.bio](https://navid.bio)
654
+ - Navid Media: [navid.media](https://navid.media)
655
+ - YouTube: [@thenavidm](https://youtube.com/@thenavidm?sub_confirmation=1) and [@thenavidai](https://youtube.com/@thenavidai?sub_confirmation=1)
656
+ - X: [@thenavidm](https://x.com/thenavidm)
657
+ - Instagram: [@thenavidm](https://instagram.com/thenavidm)
658
+ - LinkedIn: [thenavidm](https://linkedin.com/in/thenavidm)
659
+
660
+ ## Dependencies
661
+
662
+ | Library | Licence | What it does |
663
+ |---|---|---|
664
+ | [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) | MIT | The MCP protocol, stdio and HTTP transports |
665
+ | [zod](https://github.com/colinhacks/zod) | MIT | One schema per tool, driving both surfaces |
666
+
667
+ The browser connection uses Node's built-in `WebSocket` and needs nothing else.
668
+
669
+ ## License
670
+
671
+ [MIT](./LICENSE). Free to use, modify, and share.
672
+
673
+ Not affiliated with, endorsed by, or sponsored by Midjourney, Inc. Midjourney is a trademark of Midjourney, Inc.
674
+
675
+ ---
676
+
677
+ © 2026 [NM Media](https://navid.media). Made with ❤️ by [Navid Moazzez](https://navid.me).