@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.
- package/LICENSE +21 -0
- package/README.md +677 -0
- package/SKILL.md +184 -0
- package/dist/api/client.d.ts +72 -0
- package/dist/api/client.js +278 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/download.d.ts +41 -0
- package/dist/api/download.js +108 -0
- package/dist/api/download.js.map +1 -0
- package/dist/api/errors.d.ts +75 -0
- package/dist/api/errors.js +166 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/jobs.d.ts +140 -0
- package/dist/api/jobs.js +296 -0
- package/dist/api/jobs.js.map +1 -0
- package/dist/api/moodboards.d.ts +88 -0
- package/dist/api/moodboards.js +189 -0
- package/dist/api/moodboards.js.map +1 -0
- package/dist/capture.d.ts +27 -0
- package/dist/capture.js +162 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli.d.ts +92 -0
- package/dist/cli.js +633 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.js +90 -0
- package/dist/config.js.map +1 -0
- package/dist/content/prompt.d.ts +69 -0
- package/dist/content/prompt.js +173 -0
- package/dist/content/prompt.js.map +1 -0
- package/dist/doctor.d.ts +19 -0
- package/dist/doctor.js +161 -0
- package/dist/doctor.js.map +1 -0
- package/dist/format/jobs.d.ts +71 -0
- package/dist/format/jobs.js +211 -0
- package/dist/format/jobs.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +161 -0
- package/dist/index.js.map +1 -0
- package/dist/safety.d.ts +52 -0
- package/dist/safety.js +100 -0
- package/dist/safety.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +57 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/create.d.ts +96 -0
- package/dist/tools/create.js +375 -0
- package/dist/tools/create.js.map +1 -0
- package/dist/tools/download.d.ts +17 -0
- package/dist/tools/download.js +47 -0
- package/dist/tools/download.js.map +1 -0
- package/dist/tools/explore.d.ts +8 -0
- package/dist/tools/explore.js +57 -0
- package/dist/tools/explore.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +16 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/jobs.d.ts +18 -0
- package/dist/tools/jobs.js +92 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/kit.d.ts +84 -0
- package/dist/tools/kit.js +104 -0
- package/dist/tools/kit.js.map +1 -0
- package/dist/tools/library.d.ts +22 -0
- package/dist/tools/library.js +185 -0
- package/dist/tools/library.js.map +1 -0
- package/dist/tools/profile.d.ts +2 -0
- package/dist/tools/profile.js +89 -0
- package/dist/tools/profile.js.map +1 -0
- package/dist/transport/cdp.d.ts +217 -0
- package/dist/transport/cdp.js +607 -0
- package/dist/transport/cdp.js.map +1 -0
- package/dist/transport/http.d.ts +18 -0
- package/dist/transport/http.js +48 -0
- package/dist/transport/http.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Who the session is, and the personalisation attached to it. */
|
|
2
|
+
import { ENDPOINTS } from "../api/jobs.js";
|
|
3
|
+
import { listProfiles } from "../api/moodboards.js";
|
|
4
|
+
import { defineTool } from "./kit.js";
|
|
5
|
+
export const profileTools = [
|
|
6
|
+
defineTool({
|
|
7
|
+
name: "whoami",
|
|
8
|
+
title: "Show the signed-in account",
|
|
9
|
+
description: "Report which Midjourney account the controlled browser is signed in as, and whether the browser is reachable at all.\n\nThe first thing to call when something is not working: it separates 'the browser is not running', 'the browser is running but signed out' and 'the account is fine, the request was wrong', which fail in very different ways.",
|
|
10
|
+
schema: {},
|
|
11
|
+
risk: "read",
|
|
12
|
+
handler: async (_args, ctx) => {
|
|
13
|
+
const running = await ctx.client.transport.isRunning();
|
|
14
|
+
if (!running) {
|
|
15
|
+
return {
|
|
16
|
+
browser_running: false,
|
|
17
|
+
signed_in: false,
|
|
18
|
+
next: "The controlled Chrome is not running. It starts on demand, or run `midjourney-cli login` to open it and sign in.",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const version = await ctx.client.transport.version();
|
|
22
|
+
try {
|
|
23
|
+
const userId = await ctx.client.userId();
|
|
24
|
+
return {
|
|
25
|
+
browser_running: true,
|
|
26
|
+
signed_in: true,
|
|
27
|
+
user_id: userId,
|
|
28
|
+
channel_id: `singleplayer_${userId.replace(/^singleplayer_/, "")}`,
|
|
29
|
+
browser: version?.Browser,
|
|
30
|
+
profile_dir: ctx.config.profileDir,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return {
|
|
35
|
+
browser_running: true,
|
|
36
|
+
signed_in: false,
|
|
37
|
+
browser: version?.Browser,
|
|
38
|
+
profile_dir: ctx.config.profileDir,
|
|
39
|
+
reason: error.message,
|
|
40
|
+
next: "Run `midjourney-cli login` to open the controlled window and sign in. The session then persists across restarts.",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
defineTool({
|
|
46
|
+
name: "list_personalized_profiles",
|
|
47
|
+
title: "List personalisation profiles",
|
|
48
|
+
description: "List the account's personalisation profiles, with how many images each was trained on.\n\nA profile is built from the images the account has rated, and biases generations toward that taste. Pass one to imagine as `profile: \"<id>\"`. A profile with a low ranking count has little to go on, so it will barely change the result.",
|
|
49
|
+
schema: {},
|
|
50
|
+
risk: "read",
|
|
51
|
+
handler: async (_args, ctx) => {
|
|
52
|
+
const profiles = await listProfiles(ctx.client);
|
|
53
|
+
return {
|
|
54
|
+
count: profiles.length,
|
|
55
|
+
profiles: profiles.map((profile) => ({
|
|
56
|
+
id: profile.id,
|
|
57
|
+
title: profile.title,
|
|
58
|
+
...(profile.rankingCount !== undefined ? { images_rated: profile.rankingCount } : {}),
|
|
59
|
+
...(profile.majorVersion ? { model_version: profile.majorVersion } : {}),
|
|
60
|
+
})),
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
defineTool({
|
|
65
|
+
name: "list_following",
|
|
66
|
+
title: "List who this account follows",
|
|
67
|
+
description: "List the Midjourney creators this account follows.",
|
|
68
|
+
schema: {},
|
|
69
|
+
risk: "read",
|
|
70
|
+
handler: async (_args, ctx) => ctx.client.request(ENDPOINTS.following),
|
|
71
|
+
}),
|
|
72
|
+
defineTool({
|
|
73
|
+
name: "list_model_ratings",
|
|
74
|
+
title: "List pending rating tasks",
|
|
75
|
+
description: "List the image-rating tasks Midjourney is offering this account. Rating images earns fast hours and feeds the account's personalisation profile.",
|
|
76
|
+
schema: {},
|
|
77
|
+
risk: "read",
|
|
78
|
+
handler: async (_args, ctx) => ctx.client.request(ENDPOINTS.modelRatings),
|
|
79
|
+
}),
|
|
80
|
+
defineTool({
|
|
81
|
+
name: "get_contest_ranking_count",
|
|
82
|
+
title: "Show contest ranking counts",
|
|
83
|
+
description: "Show how many contest ranking rounds this account has completed.",
|
|
84
|
+
schema: {},
|
|
85
|
+
risk: "read",
|
|
86
|
+
handler: async (_args, ctx) => ctx.client.request(ENDPOINTS.contestsRankingCount),
|
|
87
|
+
}),
|
|
88
|
+
];
|
|
89
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/tools/profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome DevTools Protocol transport.
|
|
3
|
+
*
|
|
4
|
+
* This is the whole reason the server works, so it is worth saying plainly why
|
|
5
|
+
* it exists rather than a normal HTTP client.
|
|
6
|
+
*
|
|
7
|
+
* Midjourney publishes no API. The endpoints under `/api/` are the ones its own
|
|
8
|
+
* web app calls, and they sit behind a Cloudflare interstitial that answers a
|
|
9
|
+
* plain client with a 403 challenge page rather than JSON. The challenge is not
|
|
10
|
+
* defeated by a header: the `cf_clearance` cookie is bound to the IP, the
|
|
11
|
+
* User-Agent and the TLS fingerprint together, so a cookie lifted out of a
|
|
12
|
+
* browser and replayed from Node is a different client and gets stopped.
|
|
13
|
+
*
|
|
14
|
+
* Rather than impersonate a browser, we drive one. Requests are issued by
|
|
15
|
+
* `fetch()` running inside a real midjourney.com page in a real Chrome that is
|
|
16
|
+
* really logged in. Same origin, same cookies, same fingerprint, same IP,
|
|
17
|
+
* credentials attached by the browser itself. There is nothing to spoof
|
|
18
|
+
* because nothing is being faked, and the session stays valid for as long as
|
|
19
|
+
* the user stays logged in.
|
|
20
|
+
*
|
|
21
|
+
* Chrome 136 stopped honouring --remote-debugging-port on the default profile,
|
|
22
|
+
* so attaching to whatever Chrome the user already has open is not reliable.
|
|
23
|
+
* Instead we own a profile: a dedicated user-data-dir under ~/.midjourney-mcp
|
|
24
|
+
* that the user logs into once. That also keeps this well away from their real
|
|
25
|
+
* browsing session, which is the right default for something holding a login.
|
|
26
|
+
*/
|
|
27
|
+
export type CdpTarget = {
|
|
28
|
+
id: string;
|
|
29
|
+
type: string;
|
|
30
|
+
url: string;
|
|
31
|
+
title: string;
|
|
32
|
+
webSocketDebuggerUrl?: string;
|
|
33
|
+
};
|
|
34
|
+
/** What a page-side fetch came back with. */
|
|
35
|
+
export type PageResponse = {
|
|
36
|
+
ok: boolean;
|
|
37
|
+
status: number;
|
|
38
|
+
/** The response body as text. JSON parsing happens a layer up. */
|
|
39
|
+
body: string;
|
|
40
|
+
/** Present when the browser refused to make the request at all. */
|
|
41
|
+
networkError?: string;
|
|
42
|
+
};
|
|
43
|
+
export declare function findChrome(explicit?: string): string;
|
|
44
|
+
export declare function defaultProfileDir(): string;
|
|
45
|
+
/**
|
|
46
|
+
* A single WebSocket to one CDP target, multiplexing commands by id.
|
|
47
|
+
*
|
|
48
|
+
* One socket per command would be simpler and is what a lot of CDP glue does,
|
|
49
|
+
* but it costs a connection handshake on every API call and loses the events
|
|
50
|
+
* we need for navigation. Node 22 ships a global WebSocket, so this needs no
|
|
51
|
+
* dependency at all.
|
|
52
|
+
*/
|
|
53
|
+
export declare class CdpSession {
|
|
54
|
+
private socket?;
|
|
55
|
+
private nextId;
|
|
56
|
+
private readonly pending;
|
|
57
|
+
private readonly listeners;
|
|
58
|
+
private readonly url;
|
|
59
|
+
private readonly timeoutMs;
|
|
60
|
+
private closed;
|
|
61
|
+
constructor(webSocketDebuggerUrl: string, timeoutMs: number);
|
|
62
|
+
connect(): Promise<void>;
|
|
63
|
+
private receive;
|
|
64
|
+
send<T = Record<string, unknown>>(method: string, params?: Record<string, unknown>): Promise<T>;
|
|
65
|
+
/**
|
|
66
|
+
* Answer a native `window.alert`, `confirm` or `prompt`.
|
|
67
|
+
*
|
|
68
|
+
* These are not DOM elements, they are browser modals, and while one is open
|
|
69
|
+
* the renderer stops servicing the DevTools protocol entirely. Every command
|
|
70
|
+
* then hangs until its timeout, including the ones trying to clear the modal,
|
|
71
|
+
* so the reply to this one only arrives after it has already worked. That is
|
|
72
|
+
* why it is sent without awaiting.
|
|
73
|
+
*
|
|
74
|
+
* Midjourney uses one for "Add from Link", so this is a normal path, not only
|
|
75
|
+
* an error path.
|
|
76
|
+
*/
|
|
77
|
+
answerDialog(accept: boolean, promptText?: string): void;
|
|
78
|
+
/** Send a command without waiting for a reply, for use while the page is blocked. */
|
|
79
|
+
fire(method: string, params?: Record<string, unknown>): void;
|
|
80
|
+
/** Subscribe to a CDP event for as long as this session lives. */
|
|
81
|
+
on(method: string, handler: (params: unknown) => void): () => void;
|
|
82
|
+
/** Resolve on the next occurrence of a CDP event, or reject on timeout. */
|
|
83
|
+
once(method: string, timeoutMs?: number): Promise<unknown>;
|
|
84
|
+
close(): void;
|
|
85
|
+
}
|
|
86
|
+
export type BrowserOptions = {
|
|
87
|
+
/** Where DevTools is listening. */
|
|
88
|
+
cdpUrl: string;
|
|
89
|
+
/** Dedicated Chrome profile, so this never touches the user's own. */
|
|
90
|
+
profileDir: string;
|
|
91
|
+
/** Path to the Chrome binary, or undefined to search the usual places. */
|
|
92
|
+
chromePath?: string;
|
|
93
|
+
/** Start Chrome when nothing is listening. */
|
|
94
|
+
autoLaunch: boolean;
|
|
95
|
+
/** Run without a visible window. Logging in needs a window, so this defaults off. */
|
|
96
|
+
headless: boolean;
|
|
97
|
+
/** Per-command deadline. */
|
|
98
|
+
timeoutMs: number;
|
|
99
|
+
/** The origin every API call is made from. */
|
|
100
|
+
origin: string;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* The browser this server drives.
|
|
104
|
+
*
|
|
105
|
+
* Everything above the transport talks to `apiFetch` and `fetchBinary` and does
|
|
106
|
+
* not know a browser is involved. That seam is deliberate: a second transport
|
|
107
|
+
* that impersonates Chrome's TLS fingerprint can implement the same two methods
|
|
108
|
+
* for people who need this on a headless box, without any tool changing.
|
|
109
|
+
*/
|
|
110
|
+
export declare class CdpBrowser {
|
|
111
|
+
private readonly options;
|
|
112
|
+
private session?;
|
|
113
|
+
private sessionTargetId?;
|
|
114
|
+
private launched;
|
|
115
|
+
/** Text to type into the next native prompt, when one is expected. */
|
|
116
|
+
private pendingDialogText?;
|
|
117
|
+
constructor(options: BrowserOptions);
|
|
118
|
+
/** True when something is already answering on the DevTools port. */
|
|
119
|
+
isRunning(): Promise<boolean>;
|
|
120
|
+
version(): Promise<Record<string, string> | undefined>;
|
|
121
|
+
/**
|
|
122
|
+
* Start Chrome on the dedicated profile and wait for DevTools to answer.
|
|
123
|
+
*
|
|
124
|
+
* Detached and with stdio ignored, so the browser outlives this process. A
|
|
125
|
+
* server that killed the browser on exit would log the user out every time an
|
|
126
|
+
* MCP client restarted, which is most of the value gone.
|
|
127
|
+
*/
|
|
128
|
+
launch(): Promise<void>;
|
|
129
|
+
private targets;
|
|
130
|
+
private originHost;
|
|
131
|
+
/** A page already on the target origin, or undefined. */
|
|
132
|
+
private findOriginTab;
|
|
133
|
+
/**
|
|
134
|
+
* Open a new tab. Chrome switched `/json/new` to PUT-only, and older builds
|
|
135
|
+
* only accept GET, so try the current spelling and fall back.
|
|
136
|
+
*/
|
|
137
|
+
private openTab;
|
|
138
|
+
closeTab(targetId: string): Promise<void>;
|
|
139
|
+
/** A connected session against a page on the target origin, reused across calls. */
|
|
140
|
+
private originSession;
|
|
141
|
+
/**
|
|
142
|
+
* Answer the next native prompt with this text instead of dismissing it.
|
|
143
|
+
*
|
|
144
|
+
* Consumed once. Midjourney's "Add from Link" is a `window.prompt`, so
|
|
145
|
+
* driving it means having the answer ready before the click that opens it.
|
|
146
|
+
*/
|
|
147
|
+
expectDialog(text: string): void;
|
|
148
|
+
/** Evaluate an expression in the page and return its value. */
|
|
149
|
+
private evaluate;
|
|
150
|
+
/**
|
|
151
|
+
* Issue an API request from inside the page.
|
|
152
|
+
*
|
|
153
|
+
* `credentials: 'include'` is what makes this work: the browser attaches the
|
|
154
|
+
* session and Cloudflare clearance cookies itself, so nothing here ever
|
|
155
|
+
* handles a credential. The whole response is returned as text and parsed
|
|
156
|
+
* upstream, because an error page is HTML and swallowing that would lose the
|
|
157
|
+
* only clue about what went wrong.
|
|
158
|
+
*/
|
|
159
|
+
apiFetch(path: string, init?: {
|
|
160
|
+
method?: string;
|
|
161
|
+
body?: unknown;
|
|
162
|
+
headers?: Record<string, string>;
|
|
163
|
+
}): Promise<PageResponse>;
|
|
164
|
+
/**
|
|
165
|
+
* Evaluate an expression in the Midjourney page and return its value.
|
|
166
|
+
*
|
|
167
|
+
* Used for the things the API does not answer. The signed-in user's own id is
|
|
168
|
+
* the case that matters: the endpoints want it as a parameter but none of
|
|
169
|
+
* them returns it, while the app has had it in memory since it booted.
|
|
170
|
+
*/
|
|
171
|
+
evaluateInPage<T>(expression: string): Promise<T>;
|
|
172
|
+
/**
|
|
173
|
+
* Make the open window show what just happened.
|
|
174
|
+
*
|
|
175
|
+
* Requests go out through `fetch()` behind the page, so the app never learns
|
|
176
|
+
* a job was submitted: its own grid only updates for work it started itself.
|
|
177
|
+
* The window then sits there looking stale until someone reloads it by hand,
|
|
178
|
+
* which makes a tool that is working look like a tool that is broken.
|
|
179
|
+
*
|
|
180
|
+
* A reload is blunt, and it is what the app does anyway on navigation. It is
|
|
181
|
+
* fire-and-forget: never awaited into a tool result, and a failure here is
|
|
182
|
+
* cosmetic, so it must not fail a generation that already succeeded.
|
|
183
|
+
*/
|
|
184
|
+
refreshView(): Promise<void>;
|
|
185
|
+
/**
|
|
186
|
+
* Fetch a binary asset and return it base64 encoded.
|
|
187
|
+
*
|
|
188
|
+
* Done from inside the existing page, which means no window activity at all:
|
|
189
|
+
* no new tab, no navigation, no flash.
|
|
190
|
+
*
|
|
191
|
+
* The reference implementation for this API states that the CDN "disallows
|
|
192
|
+
* browser fetch() from www.midjourney.com via CORS" and falls back to
|
|
193
|
+
* screenshotting the rendered element. That is not true, at least not any
|
|
194
|
+
* more: cdn.midjourney.com answers with `access-control-allow-origin: *`, and
|
|
195
|
+
* a same-page fetch returns the exact bytes. Verified against a real asset,
|
|
196
|
+
* byte count matching the file on disk.
|
|
197
|
+
*
|
|
198
|
+
* So this reads the real image rather than a re-encoded picture of one, and
|
|
199
|
+
* it does it without touching the user's window. `openTab` remains for the
|
|
200
|
+
* navigation fallback below, for an asset on some host that does refuse.
|
|
201
|
+
*/
|
|
202
|
+
fetchBinary(url: string): Promise<{
|
|
203
|
+
base64: string;
|
|
204
|
+
mimeType: string;
|
|
205
|
+
}>;
|
|
206
|
+
private fetchBinaryInPage;
|
|
207
|
+
/**
|
|
208
|
+
* The fallback: navigate a throwaway tab and read the bytes back.
|
|
209
|
+
*
|
|
210
|
+
* Only reached when a host refuses a cross-origin read. It does open and
|
|
211
|
+
* close a tab, which is visible, so it is deliberately second.
|
|
212
|
+
*/
|
|
213
|
+
private fetchBinaryByNavigation;
|
|
214
|
+
close(): void;
|
|
215
|
+
get didLaunch(): boolean;
|
|
216
|
+
}
|
|
217
|
+
export declare function mimeFromUrl(url: string): string;
|