@zegazone_mcp/mcp 2.0.1 → 2.0.3

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/dist/ops.js CHANGED
@@ -6,6 +6,8 @@ export const OP_TOOL_MAP = {
6
6
  ui_state_get: "ui.state.get",
7
7
  ui_state_set: "ui.state.set",
8
8
  collections_list: "collections.list",
9
+ collections_get: "collections.get",
10
+ collections_get_by_slug: "collections.get_by_slug",
9
11
  collections_batch_get: "collections.batch.get",
10
12
  collections_export: "collections.export",
11
13
  collections_create: "collections.create",
@@ -38,6 +40,7 @@ export const OP_TOOL_MAP = {
38
40
  collaborators_invite_accept: "collaborators.invite.accept",
39
41
  collaborators_invite_decline: "collaborators.invite.decline",
40
42
  media_list: "media.list",
43
+ media_get: "media.get",
41
44
  media_search: "media.search",
42
45
  media_batch_list: "media.batch.list",
43
46
  media_download: "media.download",
@@ -6,6 +6,8 @@ export const TOOL_CATEGORIES = {
6
6
  ui_state_get: "UI",
7
7
  ui_state_set: "UI",
8
8
  collections_list: "Collections",
9
+ collections_get: "Collections",
10
+ collections_get_by_slug: "Collections",
9
11
  collections_batch_get: "Collections",
10
12
  collections_export: "Collections",
11
13
  collections_create: "Collections",
@@ -38,6 +40,7 @@ export const TOOL_CATEGORIES = {
38
40
  collaborators_invite_accept: "Collaboration",
39
41
  collaborators_invite_decline: "Collaboration",
40
42
  media_list: "Media",
43
+ media_get: "Media",
41
44
  media_search: "Media",
42
45
  media_batch_list: "Media",
43
46
  media_download: "Media",
@@ -66,6 +69,8 @@ export const TOOL_DESCRIPTIONS = {
66
69
  ui_state_get: "Use when you need to know which collection and media item are currently open in the user's Zegazone UI — keeps agent actions aligned with what they see.",
67
70
  ui_state_set: "Use when the agent should drive the app: open a collection, select a media item, or sync the UI to match your workflow.",
68
71
  collections_list: "Use when you need to see what collections the user owns — names, tags, sharing level, thumbnails — before uploading media, publishing, or inviting collaborators.",
72
+ collections_get: "Look up a specific collection by its ID. Use this when you know the collection ID and need its name, description, sharing status, or other details.",
73
+ collections_get_by_slug: "Resolve a collection from a public share URL handle and slug (e.g. charliefairbairn/boating). Use when the agent has a link and needs the collection id or metadata.",
69
74
  collections_batch_get: "Use when you already have a list of collection ids and need full metadata for several at once without listing everything.",
70
75
  collections_export: "Use when the user wants a portable dump of a collection (metadata and media references) for backup or migration.",
71
76
  collections_create: "Use when starting a new collection from scratch. Set name, tags, NSFW flag, and optional parent for sub-collections.",
@@ -97,8 +102,9 @@ export const TOOL_DESCRIPTIONS = {
97
102
  collaborators_invites_received: "Use when the user asks what collections they were invited to.",
98
103
  collaborators_invite_accept: "Use after the user confirms they want access to a shared collection.",
99
104
  collaborators_invite_decline: "Use when refusing a collaboration invite.",
100
- media_list: "Use to list media inside a collectionthe main way to see what's already uploaded before adding more.",
101
- media_search: "Use when filtering media within one or more collections by name, tag, or type.",
105
+ media_list: "Use to list media inside accessible collectionsfilter by collection_id, viewer type, tags, or other metadata before adding more.",
106
+ media_get: "Look up a specific media item by its ID. Use this when you know the media ID and need its description, tags, thumbnail, or other details.",
107
+ media_search: "Search for media items by name, description, or tags. Use this to find a specific item when you don't know its ID.",
102
108
  media_batch_list: "Use when you have many media ids and need rows for all of them in one call.",
103
109
  media_download: "Use when you need a time-limited download URL for a media item's main file.",
104
110
  media_create: "Use when adding new content to a collection from a URL (image, video, website, etc.). Requires viewer type.",
@@ -136,11 +136,63 @@ export const mediaDescribeSchema = z
136
136
  media_id: z.number().int().positive(),
137
137
  })
138
138
  .catchall(z.unknown());
139
+ export const mediaGetSchema = z
140
+ .object({
141
+ args: z.record(z.unknown()).optional(),
142
+ media_id: z.number().int().positive(),
143
+ })
144
+ .catchall(z.unknown());
145
+ export const collectionsGetSchema = z
146
+ .object({
147
+ args: z.record(z.unknown()).optional(),
148
+ collection_id: z.number().int().positive(),
149
+ })
150
+ .catchall(z.unknown());
151
+ export const collectionsGetBySlugSchema = z
152
+ .object({
153
+ args: z.record(z.unknown()).optional(),
154
+ handle: z.string().min(1).describe("Publish handle from the share URL, without @."),
155
+ slug: z.string().min(1).describe("Collection slug from the share URL."),
156
+ })
157
+ .catchall(z.unknown());
158
+ export const mediaSearchSchema = z
159
+ .object({
160
+ args: z.record(z.unknown()).optional(),
161
+ query: z.string().min(1).describe("Search text matched against name, description, and tags."),
162
+ collection_id: z.number().int().positive().optional(),
163
+ include_archived: z.boolean().optional(),
164
+ limit: z.number().int().positive().optional(),
165
+ offset: z.number().int().min(0).optional(),
166
+ })
167
+ .catchall(z.unknown());
168
+ export const mediaListSchema = z
169
+ .object({
170
+ args: z.record(z.unknown()).optional(),
171
+ collection_id: z.number().int().positive().optional(),
172
+ tag: z.string().optional(),
173
+ tags: tagsSchema,
174
+ name_contains: z.string().optional(),
175
+ description_contains: z.string().optional(),
176
+ type: z.string().optional(),
177
+ viewer: viewerEnum.optional(),
178
+ created_after: z.string().optional(),
179
+ created_before: z.string().optional(),
180
+ is_nsfw: z.boolean().optional(),
181
+ include_archived: z.boolean().optional(),
182
+ limit: z.number().int().positive().optional(),
183
+ offset: z.number().int().min(0).optional(),
184
+ })
185
+ .catchall(z.unknown());
139
186
  export const TYPED_TOOL_SCHEMAS = {
140
187
  collections_create: collectionsCreateSchema,
141
188
  collections_update: collectionsUpdateSchema,
189
+ collections_get: collectionsGetSchema,
190
+ collections_get_by_slug: collectionsGetBySlugSchema,
142
191
  media_create: mediaCreateSchema,
143
192
  media_update: mediaUpdateSchema,
193
+ media_list: mediaListSchema,
194
+ media_get: mediaGetSchema,
195
+ media_search: mediaSearchSchema,
144
196
  ui_state_set: uiStateSetSchema,
145
197
  media_describe: mediaDescribeSchema,
146
198
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zegazone_mcp/mcp",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "type": "module",
5
5
  "description": "MCP server wrapper for Zegazone thirdparty-v1 API",
6
6
  "publishConfig": {
@@ -13,7 +13,8 @@
13
13
  */
14
14
 
15
15
  import crypto from "node:crypto";
16
- import fs from "node:fs/promises";
16
+ import fs from "node:fs";
17
+ import fsPromises from "node:fs/promises";
17
18
  import http from "node:http";
18
19
  import path from "node:path";
19
20
  import os from "node:os";
@@ -52,6 +53,14 @@ function randomState() {
52
53
  return base64url(crypto.randomBytes(16));
53
54
  }
54
55
 
56
+ function isWsl() {
57
+ try {
58
+ return fs.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
55
64
  function openBrowser(url) {
56
65
  const plat = process.platform;
57
66
  const detached = { detached: true, stdio: "ignore" };
@@ -103,10 +112,10 @@ async function writeCredentials(filePath, tokens) {
103
112
  access_token: tokens.access_token,
104
113
  access_expires_at_ms,
105
114
  };
106
- await fs.mkdir(path.dirname(filePath), { recursive: true });
115
+ await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
107
116
  const tmp = `${filePath}.${process.pid}.tmp`;
108
- await fs.writeFile(tmp, `${JSON.stringify(doc, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
109
- await fs.rename(tmp, filePath);
117
+ await fsPromises.writeFile(tmp, `${JSON.stringify(doc, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
118
+ await fsPromises.rename(tmp, filePath);
110
119
  }
111
120
 
112
121
  function buildAuthorizeUrl(redirectUri, codeChallenge, state) {
@@ -210,9 +219,20 @@ async function main() {
210
219
  });
211
220
  });
212
221
 
213
- console.error("Opening browser for Zegazone login…");
214
- console.error(`If it does not open, visit:\n${authorizeUrl}\n`);
215
- openBrowser(authorizeUrl);
222
+ if (isWsl()) {
223
+ console.error("Running in WSL2 cannot open a browser automatically.");
224
+ console.error("Open this URL in your Windows browser to authorise:\n");
225
+ console.error(` ${authorizeUrl}\n`);
226
+ console.error("Waiting for the browser callback (up to 10 minutes)…");
227
+ } else {
228
+ console.error("Opening browser for Zegazone login…");
229
+ console.error(`If the browser does not open, visit:\n ${authorizeUrl}\n`);
230
+ openBrowser(authorizeUrl);
231
+ }
232
+
233
+ const heartbeat = setInterval(() => {
234
+ process.stderr.write(".");
235
+ }, 10_000);
216
236
 
217
237
  try {
218
238
  await done;
@@ -222,6 +242,9 @@ async function main() {
222
242
  const msg = e instanceof Error ? e.message : String(e);
223
243
  console.error(msg);
224
244
  process.exit(1);
245
+ } finally {
246
+ clearInterval(heartbeat);
247
+ process.stderr.write("\n");
225
248
  }
226
249
  }
227
250