discogs-mcp-server 0.1.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/README.md +194 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2275 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2275 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { FastMCP, imageContent, UserError } from 'fastmcp';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
// src/version.ts
|
|
7
|
+
var VERSION = "0.1.0";
|
|
8
|
+
|
|
9
|
+
// src/config.ts
|
|
10
|
+
dotenv.config();
|
|
11
|
+
var config = {
|
|
12
|
+
discogs: {
|
|
13
|
+
apiUrl: process.env.DISCOGS_API_URL || "https://api.discogs.com",
|
|
14
|
+
/* Some MCP clients can't handle large amounts of data.
|
|
15
|
+
* The client may explicitly request more at their own peril. */
|
|
16
|
+
defaultPerPage: 5,
|
|
17
|
+
mediaType: process.env.DISCOGS_MEDIA_TYPE || "application/vnd.discogs.v2.discogs+json",
|
|
18
|
+
personalAccessToken: process.env.DISCOGS_PERSONAL_ACCESS_TOKEN,
|
|
19
|
+
userAgent: process.env.DISCOGS_USER_AGENT || `DiscogsMCPServer/${VERSION}`
|
|
20
|
+
},
|
|
21
|
+
server: {
|
|
22
|
+
name: process.env.SERVER_NAME || "Discogs MCP Server",
|
|
23
|
+
port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3001
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
function validateConfig() {
|
|
27
|
+
const missingVars = [];
|
|
28
|
+
if (!process.env.DISCOGS_PERSONAL_ACCESS_TOKEN) {
|
|
29
|
+
missingVars.push("DISCOGS_PERSONAL_ACCESS_TOKEN");
|
|
30
|
+
}
|
|
31
|
+
if (missingVars.length > 0) {
|
|
32
|
+
throw new Error(`Missing required environment variables: ${missingVars.join(", ")}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
var DiscogsError = class extends Error {
|
|
36
|
+
constructor(message, status, response) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.status = status;
|
|
39
|
+
this.response = response;
|
|
40
|
+
this.name = new.target.name;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var DiscogsAuthenticationError = class extends DiscogsError {
|
|
44
|
+
constructor(message = "Authentication failed") {
|
|
45
|
+
super(message, 401, { message });
|
|
46
|
+
this.name = new.target.name;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var DiscogsMethodNotAllowedError = class extends DiscogsError {
|
|
50
|
+
constructor(message = "Method not allowed") {
|
|
51
|
+
super(message, 405, { message });
|
|
52
|
+
this.name = new.target.name;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var DiscogsPermissionError = class extends DiscogsError {
|
|
56
|
+
constructor(message = "Insufficient permissions") {
|
|
57
|
+
super(message, 403, { message });
|
|
58
|
+
this.name = new.target.name;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var DiscogsRateLimitError = class extends DiscogsError {
|
|
62
|
+
constructor(message = "Rate limit exceeded", resetAt) {
|
|
63
|
+
super(message, 429, { message, reset_at: resetAt.toISOString() });
|
|
64
|
+
this.resetAt = resetAt;
|
|
65
|
+
this.name = new.target.name;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var DiscogsResourceNotFoundError = class extends DiscogsError {
|
|
69
|
+
constructor(message = "Resource not found") {
|
|
70
|
+
super(message, 404, { message });
|
|
71
|
+
this.name = new.target.name;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var DiscogsValidationFailedError = class extends DiscogsError {
|
|
75
|
+
constructor(response) {
|
|
76
|
+
let message = "Validation failed";
|
|
77
|
+
if (response && typeof response === "object" && response !== null) {
|
|
78
|
+
const detail = response.detail;
|
|
79
|
+
if (Array.isArray(detail) && detail.length > 0 && detail[0].msg) {
|
|
80
|
+
message = detail[0].msg;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
super(message, 422, { message });
|
|
84
|
+
this.name = new.target.name;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
function createDiscogsError(status, response) {
|
|
88
|
+
switch (status) {
|
|
89
|
+
case 401:
|
|
90
|
+
return new DiscogsAuthenticationError(response?.message);
|
|
91
|
+
case 403:
|
|
92
|
+
return new DiscogsPermissionError(response?.message);
|
|
93
|
+
case 404:
|
|
94
|
+
return new DiscogsResourceNotFoundError(response?.message || "Resource");
|
|
95
|
+
case 405:
|
|
96
|
+
return new DiscogsMethodNotAllowedError(response?.message);
|
|
97
|
+
case 422:
|
|
98
|
+
return new DiscogsValidationFailedError(response);
|
|
99
|
+
case 429:
|
|
100
|
+
return new DiscogsRateLimitError(
|
|
101
|
+
response?.message,
|
|
102
|
+
new Date(response?.reset_at || Date.now() + 6e4)
|
|
103
|
+
);
|
|
104
|
+
default:
|
|
105
|
+
return new DiscogsError(response?.message || "Discogs API error", status, response);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function formatDiscogsError(error) {
|
|
109
|
+
let message;
|
|
110
|
+
if (error instanceof Error) {
|
|
111
|
+
message = error.message;
|
|
112
|
+
} else {
|
|
113
|
+
message = String(error);
|
|
114
|
+
}
|
|
115
|
+
return new UserError(message);
|
|
116
|
+
}
|
|
117
|
+
function isDiscogsError(error) {
|
|
118
|
+
return error instanceof DiscogsError;
|
|
119
|
+
}
|
|
120
|
+
var log = {
|
|
121
|
+
_log: (level, ...args) => {
|
|
122
|
+
const msg = `[${level} ${(/* @__PURE__ */ new Date()).toISOString()}] ${args.join(" ")}
|
|
123
|
+
`;
|
|
124
|
+
process.stderr.write(msg);
|
|
125
|
+
},
|
|
126
|
+
info: (...args) => {
|
|
127
|
+
log._log("INFO", ...args);
|
|
128
|
+
},
|
|
129
|
+
debug: (...args) => {
|
|
130
|
+
log._log("DEBUG", ...args);
|
|
131
|
+
},
|
|
132
|
+
warn: (...args) => {
|
|
133
|
+
log._log("WARN", ...args);
|
|
134
|
+
},
|
|
135
|
+
error: (...args) => {
|
|
136
|
+
log._log("ERROR", ...args);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var urlOrEmptySchema = () => {
|
|
140
|
+
return z.string().refine((val) => val === "" || /^https?:\/\/.+/.test(val), {
|
|
141
|
+
message: "Must be a valid URL or empty string"
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
var CurrencyCodeSchema = z.enum([
|
|
145
|
+
"USD",
|
|
146
|
+
// US Dollar
|
|
147
|
+
"GBP",
|
|
148
|
+
// British Pound
|
|
149
|
+
"EUR",
|
|
150
|
+
// Euro
|
|
151
|
+
"CAD",
|
|
152
|
+
// Canadian Dollar
|
|
153
|
+
"AUD",
|
|
154
|
+
// Australian Dollar
|
|
155
|
+
"JPY",
|
|
156
|
+
// Japanese Yen
|
|
157
|
+
"CHF",
|
|
158
|
+
// Swiss Franc
|
|
159
|
+
"MXN",
|
|
160
|
+
// Mexican Peso
|
|
161
|
+
"BRL",
|
|
162
|
+
// Brazilian Real
|
|
163
|
+
"NZD",
|
|
164
|
+
// New Zealand Dollar
|
|
165
|
+
"SEK",
|
|
166
|
+
// Swedish Krona
|
|
167
|
+
"ZAR"
|
|
168
|
+
// South African Rand
|
|
169
|
+
]);
|
|
170
|
+
var PaginatedResponseSchema = (itemSchema, resultsFieldName) => z.object({
|
|
171
|
+
pagination: z.object({
|
|
172
|
+
page: z.number().int().min(0).optional(),
|
|
173
|
+
per_page: z.number().int().min(0).optional(),
|
|
174
|
+
pages: z.number().int().min(0),
|
|
175
|
+
items: z.number().int().min(0),
|
|
176
|
+
urls: z.object({
|
|
177
|
+
first: z.string().url().optional(),
|
|
178
|
+
prev: z.string().url().optional(),
|
|
179
|
+
next: z.string().url().optional(),
|
|
180
|
+
last: z.string().url().optional()
|
|
181
|
+
})
|
|
182
|
+
}),
|
|
183
|
+
[resultsFieldName]: z.array(itemSchema)
|
|
184
|
+
});
|
|
185
|
+
var QueryParamsSchema = (validSortKeys = []) => z.object({
|
|
186
|
+
// Pagination
|
|
187
|
+
page: z.number().int().min(1).optional(),
|
|
188
|
+
per_page: z.number().int().min(1).max(100).optional(),
|
|
189
|
+
// Sorting
|
|
190
|
+
sort: z.enum(validSortKeys).optional(),
|
|
191
|
+
sort_order: z.enum(["asc", "desc"]).optional()
|
|
192
|
+
});
|
|
193
|
+
var UsernameInputSchema = z.object({
|
|
194
|
+
username: z.string().min(1, "username is required")
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// src/types/artist.ts
|
|
198
|
+
var ArtistIdParamSchema = z.object({
|
|
199
|
+
artist_id: z.number()
|
|
200
|
+
});
|
|
201
|
+
var ArtistBasicSchema = z.object({
|
|
202
|
+
id: z.number(),
|
|
203
|
+
anv: z.string(),
|
|
204
|
+
join: z.string(),
|
|
205
|
+
name: z.string(),
|
|
206
|
+
resource_url: urlOrEmptySchema(),
|
|
207
|
+
role: z.string(),
|
|
208
|
+
tracks: z.string()
|
|
209
|
+
});
|
|
210
|
+
var ArtistSchema = z.object({
|
|
211
|
+
id: z.number(),
|
|
212
|
+
aliases: z.array(
|
|
213
|
+
z.object({
|
|
214
|
+
id: z.number(),
|
|
215
|
+
name: z.string(),
|
|
216
|
+
resource_url: urlOrEmptySchema(),
|
|
217
|
+
thumbnail_url: urlOrEmptySchema().optional()
|
|
218
|
+
})
|
|
219
|
+
).optional(),
|
|
220
|
+
data_quality: z.string().optional(),
|
|
221
|
+
images: z.array(
|
|
222
|
+
z.object({
|
|
223
|
+
height: z.number().int().optional(),
|
|
224
|
+
resource_url: urlOrEmptySchema(),
|
|
225
|
+
type: z.string().optional(),
|
|
226
|
+
uri: urlOrEmptySchema(),
|
|
227
|
+
uri150: urlOrEmptySchema().optional(),
|
|
228
|
+
width: z.number().int().optional()
|
|
229
|
+
})
|
|
230
|
+
).optional(),
|
|
231
|
+
members: z.array(
|
|
232
|
+
z.object({
|
|
233
|
+
id: z.number(),
|
|
234
|
+
active: z.boolean().optional(),
|
|
235
|
+
name: z.string(),
|
|
236
|
+
resource_url: urlOrEmptySchema(),
|
|
237
|
+
thumbnail_url: urlOrEmptySchema().optional()
|
|
238
|
+
})
|
|
239
|
+
).optional(),
|
|
240
|
+
name: z.string(),
|
|
241
|
+
namevariations: z.array(z.string()).optional(),
|
|
242
|
+
profile: z.string().optional(),
|
|
243
|
+
realname: z.string().optional(),
|
|
244
|
+
releases_url: urlOrEmptySchema().optional(),
|
|
245
|
+
resource_url: urlOrEmptySchema(),
|
|
246
|
+
uri: urlOrEmptySchema().optional(),
|
|
247
|
+
urls: z.array(z.string()).optional()
|
|
248
|
+
});
|
|
249
|
+
var ArtistReleaseSchema = z.object({
|
|
250
|
+
id: z.number(),
|
|
251
|
+
artist: z.string(),
|
|
252
|
+
catno: z.string().optional(),
|
|
253
|
+
format: z.string().optional(),
|
|
254
|
+
label: z.string().optional(),
|
|
255
|
+
main_release: z.number().optional(),
|
|
256
|
+
resource_url: urlOrEmptySchema(),
|
|
257
|
+
role: z.string().optional(),
|
|
258
|
+
status: z.string().optional(),
|
|
259
|
+
stats: z.object({
|
|
260
|
+
community: z.object({
|
|
261
|
+
in_collection: z.number(),
|
|
262
|
+
in_wantlist: z.number()
|
|
263
|
+
}),
|
|
264
|
+
user: z.object({
|
|
265
|
+
in_collection: z.number(),
|
|
266
|
+
in_wantlist: z.number()
|
|
267
|
+
})
|
|
268
|
+
}).optional(),
|
|
269
|
+
thumb: urlOrEmptySchema().optional(),
|
|
270
|
+
title: z.string(),
|
|
271
|
+
trackinfo: z.string().optional(),
|
|
272
|
+
type: z.string().optional(),
|
|
273
|
+
year: z.number().optional()
|
|
274
|
+
});
|
|
275
|
+
var ArtistReleasesParamsSchema = ArtistIdParamSchema.merge(
|
|
276
|
+
QueryParamsSchema(["year", "title", "format"])
|
|
277
|
+
);
|
|
278
|
+
var ArtistReleasesSchema = PaginatedResponseSchema(ArtistReleaseSchema, "releases");
|
|
279
|
+
|
|
280
|
+
// src/services/index.ts
|
|
281
|
+
var DiscogsService = class {
|
|
282
|
+
constructor(servicePath) {
|
|
283
|
+
this.servicePath = servicePath;
|
|
284
|
+
if (!config.discogs.personalAccessToken || !config.discogs.userAgent) {
|
|
285
|
+
throw new Error("Discogs API configuration is incomplete");
|
|
286
|
+
}
|
|
287
|
+
this.baseUrl = `${config.discogs.apiUrl}${servicePath}`;
|
|
288
|
+
this.headers = {
|
|
289
|
+
Accept: config.discogs.mediaType,
|
|
290
|
+
Authorization: `Discogs token=${config.discogs.personalAccessToken}`,
|
|
291
|
+
"Content-Type": "application/json",
|
|
292
|
+
"User-Agent": config.discogs.userAgent
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
baseUrl;
|
|
296
|
+
headers;
|
|
297
|
+
async request(path, options) {
|
|
298
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
299
|
+
if (options?.params) {
|
|
300
|
+
Object.entries(options.params).forEach(([key, value]) => {
|
|
301
|
+
if (value !== void 0) {
|
|
302
|
+
url.searchParams.append(key, String(value));
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
if (!url.searchParams.has("per_page")) {
|
|
307
|
+
url.searchParams.append("per_page", String(config.discogs.defaultPerPage));
|
|
308
|
+
}
|
|
309
|
+
const response = await fetch(url.toString(), {
|
|
310
|
+
method: options?.method || "GET",
|
|
311
|
+
headers: this.headers,
|
|
312
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
313
|
+
});
|
|
314
|
+
const contentType = response.headers.get("content-type");
|
|
315
|
+
const isJson = contentType && contentType.includes("application/json");
|
|
316
|
+
let responseBody;
|
|
317
|
+
try {
|
|
318
|
+
responseBody = isJson ? await response.json() : await response.text();
|
|
319
|
+
} catch {
|
|
320
|
+
responseBody = { message: "Failed to parse response" };
|
|
321
|
+
}
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
throw createDiscogsError(response.status, responseBody);
|
|
324
|
+
}
|
|
325
|
+
return responseBody;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
var BaseUserService = class extends DiscogsService {
|
|
329
|
+
constructor() {
|
|
330
|
+
super("/users");
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// src/services/artist.ts
|
|
335
|
+
var ArtistService = class extends DiscogsService {
|
|
336
|
+
constructor() {
|
|
337
|
+
super("/artists");
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Get an artist
|
|
341
|
+
*
|
|
342
|
+
* @param params - Parameters containing the artist ID
|
|
343
|
+
* @returns {Artist} The artist information
|
|
344
|
+
* @throws {DiscogsResourceNotFoundError} If the artist cannot be found
|
|
345
|
+
* @throws {Error} If there's an unexpected error
|
|
346
|
+
*/
|
|
347
|
+
async get({ artist_id }) {
|
|
348
|
+
try {
|
|
349
|
+
const response = await this.request(`/${artist_id}`);
|
|
350
|
+
const validatedResponse = ArtistSchema.parse(response);
|
|
351
|
+
return validatedResponse;
|
|
352
|
+
} catch (error) {
|
|
353
|
+
if (isDiscogsError(error)) {
|
|
354
|
+
throw error;
|
|
355
|
+
}
|
|
356
|
+
throw new Error(`Failed to get artist: ${String(error)}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get an artist's releases
|
|
361
|
+
*
|
|
362
|
+
* @param params - Parameters containing the artist ID and pagination options
|
|
363
|
+
* @returns {ArtistReleases} The artist releases
|
|
364
|
+
* @throws {DiscogsResourceNotFoundError} If the artist cannot be found
|
|
365
|
+
* @throws {Error} If there's an unexpected error
|
|
366
|
+
*/
|
|
367
|
+
async getReleases({ artist_id, ...options }) {
|
|
368
|
+
try {
|
|
369
|
+
const response = await this.request(`/${artist_id}/releases`, {
|
|
370
|
+
params: options
|
|
371
|
+
});
|
|
372
|
+
const validatedResponse = ArtistReleasesSchema.parse(response);
|
|
373
|
+
return validatedResponse;
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (isDiscogsError(error)) {
|
|
376
|
+
throw error;
|
|
377
|
+
}
|
|
378
|
+
throw new Error(`Failed to get artist releases: ${String(error)}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
var SearchParamsSchema = z.object({
|
|
383
|
+
q: z.string().optional(),
|
|
384
|
+
type: z.enum(["artist", "label", "master", "release"]).optional(),
|
|
385
|
+
title: z.string().optional(),
|
|
386
|
+
release_title: z.string().optional(),
|
|
387
|
+
credit: z.string().optional(),
|
|
388
|
+
artist: z.string().optional(),
|
|
389
|
+
anv: z.string().optional(),
|
|
390
|
+
label: z.string().optional(),
|
|
391
|
+
genre: z.string().optional(),
|
|
392
|
+
style: z.string().optional(),
|
|
393
|
+
country: z.string().optional(),
|
|
394
|
+
year: z.string().optional(),
|
|
395
|
+
format: z.string().optional(),
|
|
396
|
+
catno: z.string().optional(),
|
|
397
|
+
barcode: z.string().optional(),
|
|
398
|
+
track: z.string().optional(),
|
|
399
|
+
submitter: z.string().optional(),
|
|
400
|
+
contributor: z.string().optional()
|
|
401
|
+
}).merge(QueryParamsSchema(["title", "artist", "year"]));
|
|
402
|
+
var SearchResultSchema = z.object({
|
|
403
|
+
id: z.number(),
|
|
404
|
+
barcode: z.array(z.string()).optional(),
|
|
405
|
+
catno: z.string().optional(),
|
|
406
|
+
community: z.object({
|
|
407
|
+
have: z.number(),
|
|
408
|
+
want: z.number()
|
|
409
|
+
}).optional(),
|
|
410
|
+
country: z.string().optional(),
|
|
411
|
+
cover_image: urlOrEmptySchema().optional(),
|
|
412
|
+
format: z.array(z.string()).optional(),
|
|
413
|
+
format_quantity: z.number().optional(),
|
|
414
|
+
formats: z.array(
|
|
415
|
+
z.object({
|
|
416
|
+
descriptions: z.array(z.string()).optional(),
|
|
417
|
+
name: z.string(),
|
|
418
|
+
qty: z.string(),
|
|
419
|
+
text: z.string().optional()
|
|
420
|
+
})
|
|
421
|
+
).optional(),
|
|
422
|
+
genre: z.array(z.string()).optional(),
|
|
423
|
+
label: z.array(z.string()).optional(),
|
|
424
|
+
master_id: z.number().nullable().optional(),
|
|
425
|
+
master_url: urlOrEmptySchema().nullable().optional(),
|
|
426
|
+
resource_url: urlOrEmptySchema(),
|
|
427
|
+
style: z.array(z.string()).optional(),
|
|
428
|
+
thumb: urlOrEmptySchema().optional(),
|
|
429
|
+
title: z.string(),
|
|
430
|
+
type: z.enum(["artist", "label", "master", "release"]),
|
|
431
|
+
uri: z.string(),
|
|
432
|
+
user_data: z.object({
|
|
433
|
+
in_collection: z.boolean(),
|
|
434
|
+
in_wantlist: z.boolean()
|
|
435
|
+
}).optional(),
|
|
436
|
+
year: z.string().optional()
|
|
437
|
+
});
|
|
438
|
+
var SearchResultsSchema = PaginatedResponseSchema(SearchResultSchema, "results");
|
|
439
|
+
|
|
440
|
+
// src/services/database.ts
|
|
441
|
+
var DatabaseService = class extends DiscogsService {
|
|
442
|
+
constructor() {
|
|
443
|
+
super("/database");
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Issue a search query to the Discogs database
|
|
447
|
+
*
|
|
448
|
+
* @param params - Search parameters
|
|
449
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
450
|
+
* @throws {Error} If the search times out or an unexpected error occurs
|
|
451
|
+
* @returns {SearchResults} Search results
|
|
452
|
+
*/
|
|
453
|
+
async search(params) {
|
|
454
|
+
try {
|
|
455
|
+
const response = await this.request("/search", { params });
|
|
456
|
+
const validatedResponse = SearchResultsSchema.parse(response);
|
|
457
|
+
return validatedResponse;
|
|
458
|
+
} catch (error) {
|
|
459
|
+
if (isDiscogsError(error)) {
|
|
460
|
+
throw error;
|
|
461
|
+
}
|
|
462
|
+
throw new Error(`Failed to search database: ${String(error)}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
var LabelIdParamSchema = z.object({
|
|
467
|
+
label_id: z.number()
|
|
468
|
+
});
|
|
469
|
+
var LabelBasicSchema = z.object({
|
|
470
|
+
id: z.number(),
|
|
471
|
+
catno: z.string(),
|
|
472
|
+
entity_type: z.string().optional(),
|
|
473
|
+
entity_type_name: z.string().optional(),
|
|
474
|
+
name: z.string(),
|
|
475
|
+
resource_url: urlOrEmptySchema()
|
|
476
|
+
});
|
|
477
|
+
var LabelSchema = z.object({
|
|
478
|
+
id: z.number(),
|
|
479
|
+
contact_info: z.string().optional(),
|
|
480
|
+
data_quality: z.string().optional(),
|
|
481
|
+
images: z.array(
|
|
482
|
+
z.object({
|
|
483
|
+
height: z.number().int().optional(),
|
|
484
|
+
resource_url: urlOrEmptySchema(),
|
|
485
|
+
type: z.string().optional(),
|
|
486
|
+
uri: urlOrEmptySchema(),
|
|
487
|
+
uri150: urlOrEmptySchema().optional(),
|
|
488
|
+
width: z.number().int().optional()
|
|
489
|
+
})
|
|
490
|
+
).optional(),
|
|
491
|
+
name: z.string(),
|
|
492
|
+
parent_label: z.object({
|
|
493
|
+
id: z.number(),
|
|
494
|
+
name: z.string(),
|
|
495
|
+
resource_url: urlOrEmptySchema()
|
|
496
|
+
}).optional(),
|
|
497
|
+
profile: z.string().optional(),
|
|
498
|
+
releases_url: urlOrEmptySchema().optional(),
|
|
499
|
+
resource_url: urlOrEmptySchema(),
|
|
500
|
+
sublabels: z.array(
|
|
501
|
+
z.object({
|
|
502
|
+
id: z.number(),
|
|
503
|
+
name: z.string(),
|
|
504
|
+
resource_url: urlOrEmptySchema()
|
|
505
|
+
})
|
|
506
|
+
).optional(),
|
|
507
|
+
uri: urlOrEmptySchema().optional(),
|
|
508
|
+
urls: z.array(z.string()).optional()
|
|
509
|
+
});
|
|
510
|
+
var LabelReleasesParamsSchema = LabelIdParamSchema.merge(QueryParamsSchema());
|
|
511
|
+
var LabelReleasesSchema = PaginatedResponseSchema(ArtistReleaseSchema, "releases");
|
|
512
|
+
|
|
513
|
+
// src/services/label.ts
|
|
514
|
+
var LabelService = class extends DiscogsService {
|
|
515
|
+
constructor() {
|
|
516
|
+
super("/labels");
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Get a label
|
|
520
|
+
*
|
|
521
|
+
* @param params - Parameters containing the label ID
|
|
522
|
+
* @returns {Label} The label information
|
|
523
|
+
* @throws {DiscogsResourceNotFoundError} If the label cannot be found
|
|
524
|
+
* @throws {Error} If there's an unexpected error
|
|
525
|
+
*/
|
|
526
|
+
async get({ label_id }) {
|
|
527
|
+
try {
|
|
528
|
+
const response = await this.request(`/${label_id}`);
|
|
529
|
+
const validatedResponse = LabelSchema.parse(response);
|
|
530
|
+
return validatedResponse;
|
|
531
|
+
} catch (error) {
|
|
532
|
+
if (isDiscogsError(error)) {
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
throw new Error(`Failed to get label: ${String(error)}`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Returns a list of Releases associated with the label
|
|
540
|
+
*
|
|
541
|
+
* @param params - Parameters containing the label ID and pagination options
|
|
542
|
+
* @returns {LabelReleases} The label releases
|
|
543
|
+
* @throws {DiscogsResourceNotFoundError} If the label cannot be found
|
|
544
|
+
* @throws {Error} If there's an unexpected error
|
|
545
|
+
*/
|
|
546
|
+
async getReleases({ label_id, ...params }) {
|
|
547
|
+
try {
|
|
548
|
+
const response = await this.request(`/${label_id}/releases`, {
|
|
549
|
+
params
|
|
550
|
+
});
|
|
551
|
+
const validatedResponse = LabelReleasesSchema.parse(response);
|
|
552
|
+
return validatedResponse;
|
|
553
|
+
} catch (error) {
|
|
554
|
+
if (isDiscogsError(error)) {
|
|
555
|
+
throw error;
|
|
556
|
+
}
|
|
557
|
+
throw new Error(`Failed to get label releases: ${String(error)}`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
var ReleaseFormatSchema = z.object({
|
|
562
|
+
descriptions: z.array(z.string()).optional(),
|
|
563
|
+
name: z.string(),
|
|
564
|
+
qty: z.string(),
|
|
565
|
+
text: z.string().optional()
|
|
566
|
+
});
|
|
567
|
+
var BasicInformationSchema = z.object({
|
|
568
|
+
id: z.number(),
|
|
569
|
+
artists: z.array(ArtistBasicSchema),
|
|
570
|
+
cover_image: urlOrEmptySchema(),
|
|
571
|
+
formats: z.array(ReleaseFormatSchema),
|
|
572
|
+
genres: z.array(z.string()).optional(),
|
|
573
|
+
master_id: z.number().optional(),
|
|
574
|
+
master_url: urlOrEmptySchema().nullable().optional(),
|
|
575
|
+
labels: z.array(LabelBasicSchema),
|
|
576
|
+
resource_url: urlOrEmptySchema(),
|
|
577
|
+
styles: z.array(z.string()).optional(),
|
|
578
|
+
thumb: urlOrEmptySchema(),
|
|
579
|
+
title: z.string(),
|
|
580
|
+
year: z.number()
|
|
581
|
+
});
|
|
582
|
+
var ReleaseSchema = z.object({
|
|
583
|
+
id: z.number().int(),
|
|
584
|
+
artists_sort: z.string().optional(),
|
|
585
|
+
artists: z.array(ArtistBasicSchema),
|
|
586
|
+
blocked_from_sale: z.boolean().optional(),
|
|
587
|
+
companies: z.array(
|
|
588
|
+
z.object({
|
|
589
|
+
id: z.number().int().optional(),
|
|
590
|
+
catno: z.string().optional(),
|
|
591
|
+
entity_type: z.string().optional(),
|
|
592
|
+
entity_type_name: z.string().optional(),
|
|
593
|
+
name: z.string().optional(),
|
|
594
|
+
resource_url: urlOrEmptySchema().optional(),
|
|
595
|
+
thumbnail_url: urlOrEmptySchema().optional()
|
|
596
|
+
})
|
|
597
|
+
).optional(),
|
|
598
|
+
community: z.object({
|
|
599
|
+
contributors: z.array(
|
|
600
|
+
z.object({
|
|
601
|
+
resource_url: urlOrEmptySchema().optional(),
|
|
602
|
+
username: z.string().optional()
|
|
603
|
+
})
|
|
604
|
+
).optional(),
|
|
605
|
+
data_quality: z.string().optional(),
|
|
606
|
+
have: z.number().int().optional(),
|
|
607
|
+
rating: z.object({
|
|
608
|
+
average: z.number().optional(),
|
|
609
|
+
count: z.number().int().optional()
|
|
610
|
+
}).optional(),
|
|
611
|
+
status: z.string().optional(),
|
|
612
|
+
submitter: z.object({
|
|
613
|
+
resource_url: urlOrEmptySchema().optional(),
|
|
614
|
+
username: z.string().optional()
|
|
615
|
+
}).optional(),
|
|
616
|
+
want: z.number().int().optional()
|
|
617
|
+
}).optional(),
|
|
618
|
+
country: z.string().optional(),
|
|
619
|
+
data_quality: z.string().optional(),
|
|
620
|
+
date_added: z.string().optional(),
|
|
621
|
+
date_changed: z.string().optional(),
|
|
622
|
+
estimated_weight: z.number().int().optional(),
|
|
623
|
+
extraartists: z.array(ArtistBasicSchema).optional(),
|
|
624
|
+
format_quantity: z.number().int().optional(),
|
|
625
|
+
formats: z.array(ReleaseFormatSchema).optional(),
|
|
626
|
+
genres: z.array(z.string()).optional(),
|
|
627
|
+
identifiers: z.array(
|
|
628
|
+
z.object({
|
|
629
|
+
type: z.string(),
|
|
630
|
+
value: z.string(),
|
|
631
|
+
description: z.string().optional()
|
|
632
|
+
})
|
|
633
|
+
).optional(),
|
|
634
|
+
images: z.array(
|
|
635
|
+
z.object({
|
|
636
|
+
height: z.number().int().optional(),
|
|
637
|
+
width: z.number().int().optional(),
|
|
638
|
+
resource_url: urlOrEmptySchema(),
|
|
639
|
+
type: z.string().optional(),
|
|
640
|
+
uri: urlOrEmptySchema().optional(),
|
|
641
|
+
uri150: urlOrEmptySchema().optional()
|
|
642
|
+
})
|
|
643
|
+
).optional(),
|
|
644
|
+
labels: z.array(LabelBasicSchema).optional(),
|
|
645
|
+
lowest_price: z.number().optional(),
|
|
646
|
+
master_id: z.number().optional(),
|
|
647
|
+
master_url: urlOrEmptySchema().optional(),
|
|
648
|
+
notes: z.string().optional(),
|
|
649
|
+
num_for_sale: z.number().int().optional(),
|
|
650
|
+
released: z.string().optional(),
|
|
651
|
+
released_formatted: z.string().optional(),
|
|
652
|
+
resource_url: urlOrEmptySchema(),
|
|
653
|
+
series: z.array(z.unknown()).optional(),
|
|
654
|
+
status: z.string().optional(),
|
|
655
|
+
styles: z.array(z.string()).optional(),
|
|
656
|
+
thumb: urlOrEmptySchema().optional(),
|
|
657
|
+
title: z.string(),
|
|
658
|
+
tracklist: z.array(
|
|
659
|
+
z.object({
|
|
660
|
+
duration: z.string().optional(),
|
|
661
|
+
position: z.string(),
|
|
662
|
+
title: z.string(),
|
|
663
|
+
type_: z.string().optional(),
|
|
664
|
+
extraartists: z.array(ArtistBasicSchema).optional()
|
|
665
|
+
})
|
|
666
|
+
).optional(),
|
|
667
|
+
uri: urlOrEmptySchema().optional(),
|
|
668
|
+
videos: z.array(
|
|
669
|
+
z.object({
|
|
670
|
+
description: z.string().nullable().optional(),
|
|
671
|
+
duration: z.number().int().optional(),
|
|
672
|
+
embed: z.boolean().optional(),
|
|
673
|
+
title: z.string().optional(),
|
|
674
|
+
uri: urlOrEmptySchema().optional()
|
|
675
|
+
})
|
|
676
|
+
).optional(),
|
|
677
|
+
year: z.number()
|
|
678
|
+
});
|
|
679
|
+
var ReleaseIdParamSchema = z.object({
|
|
680
|
+
release_id: z.number().min(1, "The release_id must be non-zero")
|
|
681
|
+
});
|
|
682
|
+
var ReleaseParamsSchema = ReleaseIdParamSchema.extend({
|
|
683
|
+
curr_abbr: CurrencyCodeSchema.optional()
|
|
684
|
+
});
|
|
685
|
+
var ReleaseRatingSchema = UsernameInputSchema.merge(ReleaseIdParamSchema).extend({
|
|
686
|
+
rating: z.number()
|
|
687
|
+
});
|
|
688
|
+
var ReleaseRatingCommunitySchema = ReleaseIdParamSchema.extend({
|
|
689
|
+
rating: z.object({
|
|
690
|
+
average: z.number(),
|
|
691
|
+
count: z.number().int()
|
|
692
|
+
})
|
|
693
|
+
});
|
|
694
|
+
var ReleaseRatingParamsSchema = UsernameInputSchema.merge(ReleaseIdParamSchema);
|
|
695
|
+
var ReleaseRatingEditParamsSchema = ReleaseRatingParamsSchema.extend({
|
|
696
|
+
rating: z.number().int().min(1, "The rating must be at least 1").max(5, "The rating must be at most 5")
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
// src/types/master.ts
|
|
700
|
+
var MasterReleaseIdParamSchema = z.object({
|
|
701
|
+
master_id: z.number().int()
|
|
702
|
+
});
|
|
703
|
+
var MasterReleaseSchema = ReleaseSchema.extend({
|
|
704
|
+
main_release: z.number(),
|
|
705
|
+
most_recent_release: z.number(),
|
|
706
|
+
versions_url: urlOrEmptySchema(),
|
|
707
|
+
main_release_url: urlOrEmptySchema(),
|
|
708
|
+
most_recent_release_url: urlOrEmptySchema(),
|
|
709
|
+
tracklist: z.array(
|
|
710
|
+
z.object({
|
|
711
|
+
position: z.string(),
|
|
712
|
+
type_: z.string().optional(),
|
|
713
|
+
title: z.string(),
|
|
714
|
+
duration: z.string().optional(),
|
|
715
|
+
extraartists: z.array(ArtistBasicSchema).optional()
|
|
716
|
+
})
|
|
717
|
+
).optional(),
|
|
718
|
+
artists: z.array(
|
|
719
|
+
ArtistBasicSchema.extend({
|
|
720
|
+
thumbnail_url: urlOrEmptySchema().optional()
|
|
721
|
+
})
|
|
722
|
+
)
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
// src/services/master.ts
|
|
726
|
+
var MasterReleaseService = class extends DiscogsService {
|
|
727
|
+
constructor() {
|
|
728
|
+
super("/masters");
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Get a master release
|
|
732
|
+
*
|
|
733
|
+
* @param params - Parameters containing the master release ID
|
|
734
|
+
* @returns {MasterRelease} The master release information
|
|
735
|
+
* @throws {DiscogsResourceNotFoundError} If the master release cannot be found
|
|
736
|
+
* @throws {Error} If there's an unexpected error
|
|
737
|
+
*/
|
|
738
|
+
async get({ master_id }) {
|
|
739
|
+
try {
|
|
740
|
+
const response = await this.request(`/${master_id}`);
|
|
741
|
+
const validatedResponse = MasterReleaseSchema.parse(response);
|
|
742
|
+
return validatedResponse;
|
|
743
|
+
} catch (error) {
|
|
744
|
+
if (isDiscogsError(error)) {
|
|
745
|
+
throw error;
|
|
746
|
+
}
|
|
747
|
+
throw new Error(`Failed to get master release: ${String(error)}`);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
// src/services/release.ts
|
|
753
|
+
var ReleaseService = class extends DiscogsService {
|
|
754
|
+
constructor() {
|
|
755
|
+
super("/releases");
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Deletes the release's rating for a given user
|
|
759
|
+
*
|
|
760
|
+
* @param params - Parameters for the request
|
|
761
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
762
|
+
* @throws {DiscogsPermissionError} If trying to delete a release rating of another user
|
|
763
|
+
* @throws {DiscogsResourceNotFoundError} If the release or user cannot be found
|
|
764
|
+
* @throws {Error} If there's an unexpected error
|
|
765
|
+
*/
|
|
766
|
+
async deleteRatingByUser({ username, release_id }) {
|
|
767
|
+
try {
|
|
768
|
+
await this.request(`/${release_id}/rating/${username}`, {
|
|
769
|
+
method: "DELETE"
|
|
770
|
+
});
|
|
771
|
+
} catch (error) {
|
|
772
|
+
if (isDiscogsError(error)) {
|
|
773
|
+
throw error;
|
|
774
|
+
}
|
|
775
|
+
throw new Error(`Failed to delete release rating: ${String(error)}`);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Updates the release's rating for a given user
|
|
780
|
+
*
|
|
781
|
+
* @param params - Parameters for the request
|
|
782
|
+
* @returns {ReleaseRating} The updated release rating
|
|
783
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
784
|
+
* @throws {DiscogsPermissionError} If trying to edit a release rating of another user
|
|
785
|
+
* @throws {DiscogsResourceNotFoundError} If the release or user cannot be found
|
|
786
|
+
* @throws {Error} If there's an unexpected error
|
|
787
|
+
*/
|
|
788
|
+
async editRatingByUser({
|
|
789
|
+
username,
|
|
790
|
+
release_id,
|
|
791
|
+
rating
|
|
792
|
+
}) {
|
|
793
|
+
try {
|
|
794
|
+
const response = await this.request(`/${release_id}/rating/${username}`, {
|
|
795
|
+
method: "PUT",
|
|
796
|
+
body: { rating }
|
|
797
|
+
});
|
|
798
|
+
const validatedResponse = ReleaseRatingSchema.parse(response);
|
|
799
|
+
return validatedResponse;
|
|
800
|
+
} catch (error) {
|
|
801
|
+
if (isDiscogsError(error)) {
|
|
802
|
+
throw error;
|
|
803
|
+
}
|
|
804
|
+
throw new Error(`Failed to edit release rating: ${String(error)}`);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Get a release
|
|
809
|
+
*
|
|
810
|
+
* @param params - Parameters for the request
|
|
811
|
+
* @returns {Release} The release information
|
|
812
|
+
* @throws {DiscogsResourceNotFoundError} If the release cannot be found
|
|
813
|
+
* @throws {Error} If there's an unexpected error
|
|
814
|
+
*/
|
|
815
|
+
async get({ release_id, ...options }) {
|
|
816
|
+
try {
|
|
817
|
+
const response = await this.request(`/${release_id}`, {
|
|
818
|
+
params: options
|
|
819
|
+
});
|
|
820
|
+
const validatedResponse = ReleaseSchema.parse(response);
|
|
821
|
+
return validatedResponse;
|
|
822
|
+
} catch (error) {
|
|
823
|
+
if (isDiscogsError(error)) {
|
|
824
|
+
throw error;
|
|
825
|
+
}
|
|
826
|
+
throw new Error(`Failed to get release: ${String(error)}`);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Retrieves the community release rating average and count
|
|
831
|
+
*
|
|
832
|
+
* @param params - Parameters for the request
|
|
833
|
+
* @returns {ReleaseRatingCommunity} The release community rating
|
|
834
|
+
* @throws {DiscogsResourceNotFoundError} If the release cannot be found
|
|
835
|
+
* @throws {Error} If there's an unexpected error
|
|
836
|
+
*/
|
|
837
|
+
async getCommunityRating({ release_id }) {
|
|
838
|
+
try {
|
|
839
|
+
const response = await this.request(`/${release_id}/rating`);
|
|
840
|
+
const validatedResponse = ReleaseRatingCommunitySchema.parse(response);
|
|
841
|
+
return validatedResponse;
|
|
842
|
+
} catch (error) {
|
|
843
|
+
if (isDiscogsError(error)) {
|
|
844
|
+
throw error;
|
|
845
|
+
}
|
|
846
|
+
throw new Error(`Failed to get release community rating: ${String(error)}`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Retrieves the release's rating for a given user
|
|
851
|
+
*
|
|
852
|
+
* @param params - Parameters for the request
|
|
853
|
+
* @returns {ReleaseRating} The release rating
|
|
854
|
+
* @throws {DiscogsResourceNotFoundError} If the release or user cannot be found
|
|
855
|
+
* @throws {Error} If there's an unexpected error
|
|
856
|
+
*/
|
|
857
|
+
async getRatingByUser({ username, release_id }) {
|
|
858
|
+
try {
|
|
859
|
+
const response = await this.request(`/${release_id}/rating/${username}`);
|
|
860
|
+
const validatedResponse = ReleaseRatingSchema.parse(response);
|
|
861
|
+
return validatedResponse;
|
|
862
|
+
} catch (error) {
|
|
863
|
+
if (isDiscogsError(error)) {
|
|
864
|
+
throw error;
|
|
865
|
+
}
|
|
866
|
+
throw new Error(`Failed to get release rating: ${String(error)}`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
// src/tools/database.ts
|
|
872
|
+
var deleteReleaseRatingTool = {
|
|
873
|
+
name: "delete_release_rating",
|
|
874
|
+
description: `Deletes the release's rating for a given user`,
|
|
875
|
+
parameters: ReleaseRatingParamsSchema,
|
|
876
|
+
execute: async (args) => {
|
|
877
|
+
try {
|
|
878
|
+
const releaseService = new ReleaseService();
|
|
879
|
+
await releaseService.deleteRatingByUser(args);
|
|
880
|
+
return "Release rating deleted successfully";
|
|
881
|
+
} catch (error) {
|
|
882
|
+
throw formatDiscogsError(error);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
var editReleaseRatingTool = {
|
|
887
|
+
name: "edit_release_rating",
|
|
888
|
+
description: `Updates the release's rating for a given user`,
|
|
889
|
+
parameters: ReleaseRatingEditParamsSchema,
|
|
890
|
+
execute: async (args) => {
|
|
891
|
+
try {
|
|
892
|
+
const releaseService = new ReleaseService();
|
|
893
|
+
const releaseRating = await releaseService.editRatingByUser(args);
|
|
894
|
+
return JSON.stringify(releaseRating);
|
|
895
|
+
} catch (error) {
|
|
896
|
+
throw formatDiscogsError(error);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
var getArtistTool = {
|
|
901
|
+
name: "get_artist",
|
|
902
|
+
description: "Get an artist",
|
|
903
|
+
parameters: ArtistIdParamSchema,
|
|
904
|
+
execute: async (args) => {
|
|
905
|
+
try {
|
|
906
|
+
const artistService = new ArtistService();
|
|
907
|
+
const artist = await artistService.get(args);
|
|
908
|
+
return JSON.stringify(artist);
|
|
909
|
+
} catch (error) {
|
|
910
|
+
throw formatDiscogsError(error);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
var getArtistReleasesTool = {
|
|
915
|
+
name: "get_artist_releases",
|
|
916
|
+
description: `Get an artist's releases`,
|
|
917
|
+
parameters: ArtistReleasesParamsSchema,
|
|
918
|
+
execute: async (args) => {
|
|
919
|
+
try {
|
|
920
|
+
const artistService = new ArtistService();
|
|
921
|
+
const artistReleases = await artistService.getReleases(args);
|
|
922
|
+
return JSON.stringify(artistReleases);
|
|
923
|
+
} catch (error) {
|
|
924
|
+
throw formatDiscogsError(error);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
var getLabelTool = {
|
|
929
|
+
name: "get_label",
|
|
930
|
+
description: "Get a label",
|
|
931
|
+
parameters: LabelIdParamSchema,
|
|
932
|
+
execute: async (args) => {
|
|
933
|
+
try {
|
|
934
|
+
const labelService = new LabelService();
|
|
935
|
+
const label = await labelService.get(args);
|
|
936
|
+
return JSON.stringify(label);
|
|
937
|
+
} catch (error) {
|
|
938
|
+
throw formatDiscogsError(error);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
var getLabelReleasesTool = {
|
|
943
|
+
name: "get_label_releases",
|
|
944
|
+
description: "Returns a list of Releases associated with the label",
|
|
945
|
+
parameters: LabelReleasesParamsSchema,
|
|
946
|
+
execute: async (args) => {
|
|
947
|
+
try {
|
|
948
|
+
const labelService = new LabelService();
|
|
949
|
+
const labelReleases = await labelService.getReleases(args);
|
|
950
|
+
return JSON.stringify(labelReleases);
|
|
951
|
+
} catch (error) {
|
|
952
|
+
throw formatDiscogsError(error);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
var getMasterReleaseTool = {
|
|
957
|
+
name: "get_master_release",
|
|
958
|
+
description: "Get a master release",
|
|
959
|
+
parameters: MasterReleaseIdParamSchema,
|
|
960
|
+
execute: async (args) => {
|
|
961
|
+
try {
|
|
962
|
+
const masterReleaseService = new MasterReleaseService();
|
|
963
|
+
const masterRelease = await masterReleaseService.get(args);
|
|
964
|
+
return JSON.stringify(masterRelease);
|
|
965
|
+
} catch (error) {
|
|
966
|
+
throw formatDiscogsError(error);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
var getReleaseTool = {
|
|
971
|
+
name: "get_release",
|
|
972
|
+
description: "Get a release",
|
|
973
|
+
parameters: ReleaseParamsSchema,
|
|
974
|
+
execute: async (args) => {
|
|
975
|
+
try {
|
|
976
|
+
const releaseService = new ReleaseService();
|
|
977
|
+
const release = await releaseService.get(args);
|
|
978
|
+
return JSON.stringify(release);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
throw formatDiscogsError(error);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
var getReleaseCommunityRatingTool = {
|
|
985
|
+
name: "get_release_community_rating",
|
|
986
|
+
description: "Retrieves the release community rating average and count",
|
|
987
|
+
parameters: ReleaseIdParamSchema,
|
|
988
|
+
execute: async (args) => {
|
|
989
|
+
try {
|
|
990
|
+
const releaseService = new ReleaseService();
|
|
991
|
+
const releaseRating = await releaseService.getCommunityRating(args);
|
|
992
|
+
return JSON.stringify(releaseRating);
|
|
993
|
+
} catch (error) {
|
|
994
|
+
throw formatDiscogsError(error);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
var getReleaseRatingTool = {
|
|
999
|
+
name: "get_release_rating_by_user",
|
|
1000
|
+
description: `Retrieves the release's rating for a given user`,
|
|
1001
|
+
parameters: ReleaseRatingParamsSchema,
|
|
1002
|
+
execute: async (args) => {
|
|
1003
|
+
try {
|
|
1004
|
+
const releaseService = new ReleaseService();
|
|
1005
|
+
const releaseRating = await releaseService.getRatingByUser(args);
|
|
1006
|
+
return JSON.stringify(releaseRating);
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
throw formatDiscogsError(error);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
var searchTool = {
|
|
1013
|
+
name: "search",
|
|
1014
|
+
description: "Issue a search query to the Discogs database",
|
|
1015
|
+
parameters: SearchParamsSchema,
|
|
1016
|
+
execute: async (args) => {
|
|
1017
|
+
try {
|
|
1018
|
+
const databaseService = new DatabaseService();
|
|
1019
|
+
const searchResults = await databaseService.search(args);
|
|
1020
|
+
return JSON.stringify(searchResults);
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
throw formatDiscogsError(error);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
function registerDatabaseTools(server) {
|
|
1027
|
+
server.addTool(getReleaseTool);
|
|
1028
|
+
server.addTool(getReleaseRatingTool);
|
|
1029
|
+
server.addTool(editReleaseRatingTool);
|
|
1030
|
+
server.addTool(deleteReleaseRatingTool);
|
|
1031
|
+
server.addTool(getReleaseCommunityRatingTool);
|
|
1032
|
+
server.addTool(getMasterReleaseTool);
|
|
1033
|
+
server.addTool(getArtistTool);
|
|
1034
|
+
server.addTool(getArtistReleasesTool);
|
|
1035
|
+
server.addTool(getLabelTool);
|
|
1036
|
+
server.addTool(getLabelReleasesTool);
|
|
1037
|
+
server.addTool(searchTool);
|
|
1038
|
+
}
|
|
1039
|
+
var MediaParamsSchema = z.object({
|
|
1040
|
+
url: z.string().url()
|
|
1041
|
+
});
|
|
1042
|
+
var fetchImageTool = {
|
|
1043
|
+
name: "fetch_image",
|
|
1044
|
+
description: "Fetch an image by URL",
|
|
1045
|
+
parameters: MediaParamsSchema,
|
|
1046
|
+
execute: async ({ url }) => {
|
|
1047
|
+
try {
|
|
1048
|
+
return imageContent({ url });
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
throw formatDiscogsError(error);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
function registerMediaTools(server) {
|
|
1055
|
+
server.addTool(fetchImageTool);
|
|
1056
|
+
}
|
|
1057
|
+
var FolderIdParamSchema = (min) => z.object({
|
|
1058
|
+
folder_id: z.number().int().min(min ?? 0)
|
|
1059
|
+
});
|
|
1060
|
+
var UserCollectionCustomFieldsSchema = z.object({
|
|
1061
|
+
fields: z.array(
|
|
1062
|
+
z.object({
|
|
1063
|
+
id: z.number().int(),
|
|
1064
|
+
lines: z.number().int().optional(),
|
|
1065
|
+
name: z.string(),
|
|
1066
|
+
options: z.array(z.string()).optional(),
|
|
1067
|
+
position: z.number().int(),
|
|
1068
|
+
public: z.boolean(),
|
|
1069
|
+
type: z.string()
|
|
1070
|
+
})
|
|
1071
|
+
)
|
|
1072
|
+
});
|
|
1073
|
+
var UserCollectionFolderSchema = z.object({
|
|
1074
|
+
id: z.number(),
|
|
1075
|
+
count: z.number(),
|
|
1076
|
+
name: z.string(),
|
|
1077
|
+
resource_url: z.string().url()
|
|
1078
|
+
});
|
|
1079
|
+
var UserCollectionFoldersSchema = z.object({
|
|
1080
|
+
folders: z.array(UserCollectionFolderSchema)
|
|
1081
|
+
});
|
|
1082
|
+
var UserCollectionFolderCreateParamsSchema = UsernameInputSchema.extend({
|
|
1083
|
+
name: z.string().optional()
|
|
1084
|
+
});
|
|
1085
|
+
var UserCollectionFolderEditParamsSchema = UserCollectionFolderCreateParamsSchema.merge(FolderIdParamSchema());
|
|
1086
|
+
var UserCollectionFolderParamsSchema = UsernameInputSchema.merge(FolderIdParamSchema());
|
|
1087
|
+
var UserCollectionFolderReleaseParamsSchema = UsernameInputSchema.merge(
|
|
1088
|
+
FolderIdParamSchema(1).merge(ReleaseIdParamSchema)
|
|
1089
|
+
);
|
|
1090
|
+
var UserCollectionItemsParamsSchema = UsernameInputSchema.merge(
|
|
1091
|
+
FolderIdParamSchema().merge(
|
|
1092
|
+
QueryParamsSchema([
|
|
1093
|
+
"added",
|
|
1094
|
+
"artist",
|
|
1095
|
+
"catno",
|
|
1096
|
+
"format",
|
|
1097
|
+
"label",
|
|
1098
|
+
"rating",
|
|
1099
|
+
"title",
|
|
1100
|
+
"year"
|
|
1101
|
+
])
|
|
1102
|
+
)
|
|
1103
|
+
);
|
|
1104
|
+
var UserCollectionReleaseAddedSchema = z.object({
|
|
1105
|
+
instance_id: z.number().int(),
|
|
1106
|
+
resource_url: z.string().url()
|
|
1107
|
+
});
|
|
1108
|
+
var UserCollectionReleaseParamsSchema = UsernameInputSchema.merge(
|
|
1109
|
+
ReleaseIdParamSchema.merge(QueryParamsSchema())
|
|
1110
|
+
);
|
|
1111
|
+
var UserCollectionReleaseDeletedParamsSchema = UserCollectionFolderReleaseParamsSchema.extend({
|
|
1112
|
+
instance_id: z.number().int()
|
|
1113
|
+
});
|
|
1114
|
+
var UserCollectionReleaseItemSchema = z.object({
|
|
1115
|
+
id: z.number(),
|
|
1116
|
+
instance_id: z.number(),
|
|
1117
|
+
basic_information: BasicInformationSchema,
|
|
1118
|
+
date_added: z.string().optional(),
|
|
1119
|
+
folder_id: z.number(),
|
|
1120
|
+
notes: z.array(
|
|
1121
|
+
z.object({
|
|
1122
|
+
field_id: z.number(),
|
|
1123
|
+
value: z.string()
|
|
1124
|
+
})
|
|
1125
|
+
).optional(),
|
|
1126
|
+
rating: z.number().int().optional()
|
|
1127
|
+
});
|
|
1128
|
+
var UserCollectionReleaseRatingParamsSchema = UserCollectionReleaseDeletedParamsSchema.extend({
|
|
1129
|
+
rating: z.number().int().min(1).max(5).optional()
|
|
1130
|
+
});
|
|
1131
|
+
var UserCollectionMoveReleaseParamsSchema = UserCollectionReleaseDeletedParamsSchema.extend({
|
|
1132
|
+
destination_folder_id: z.number()
|
|
1133
|
+
});
|
|
1134
|
+
var UserCollectionItemsByReleaseSchema = PaginatedResponseSchema(
|
|
1135
|
+
UserCollectionReleaseItemSchema,
|
|
1136
|
+
"releases"
|
|
1137
|
+
);
|
|
1138
|
+
var UserCollectionValueSchema = z.object({
|
|
1139
|
+
maximum: z.string(),
|
|
1140
|
+
median: z.string(),
|
|
1141
|
+
minimum: z.string()
|
|
1142
|
+
});
|
|
1143
|
+
var UserListItemSchema = z.object({
|
|
1144
|
+
id: z.number(),
|
|
1145
|
+
date_added: z.string(),
|
|
1146
|
+
date_changed: z.string(),
|
|
1147
|
+
description: z.string().optional(),
|
|
1148
|
+
name: z.string(),
|
|
1149
|
+
public: z.boolean(),
|
|
1150
|
+
resource_url: z.string().url(),
|
|
1151
|
+
uri: z.string().url()
|
|
1152
|
+
});
|
|
1153
|
+
var UserListsParamsSchema = UsernameInputSchema.merge(QueryParamsSchema());
|
|
1154
|
+
var UserListsSchema = PaginatedResponseSchema(UserListItemSchema, "lists");
|
|
1155
|
+
var UserProfileSchema = z.object({
|
|
1156
|
+
id: z.number(),
|
|
1157
|
+
resource_url: z.string().url(),
|
|
1158
|
+
uri: z.string().url(),
|
|
1159
|
+
username: z.string(),
|
|
1160
|
+
name: z.string(),
|
|
1161
|
+
home_page: z.string(),
|
|
1162
|
+
location: z.string(),
|
|
1163
|
+
profile: z.string(),
|
|
1164
|
+
registered: z.string(),
|
|
1165
|
+
rank: z.number(),
|
|
1166
|
+
num_pending: z.number(),
|
|
1167
|
+
num_for_sale: z.number(),
|
|
1168
|
+
num_lists: z.number(),
|
|
1169
|
+
releases_contributed: z.number(),
|
|
1170
|
+
releases_rated: z.number(),
|
|
1171
|
+
rating_avg: z.number(),
|
|
1172
|
+
inventory_url: z.string().url(),
|
|
1173
|
+
collection_folders_url: z.string().url(),
|
|
1174
|
+
collection_fields_url: z.string().url(),
|
|
1175
|
+
wantlist_url: z.string().url(),
|
|
1176
|
+
avatar_url: z.string().url(),
|
|
1177
|
+
curr_abbr: z.string(),
|
|
1178
|
+
activated: z.boolean(),
|
|
1179
|
+
marketplace_suspended: z.boolean(),
|
|
1180
|
+
banner_url: z.string(),
|
|
1181
|
+
buyer_rating: z.number(),
|
|
1182
|
+
buyer_rating_stars: z.number(),
|
|
1183
|
+
buyer_num_ratings: z.number(),
|
|
1184
|
+
seller_rating: z.number(),
|
|
1185
|
+
seller_rating_stars: z.number(),
|
|
1186
|
+
seller_num_ratings: z.number(),
|
|
1187
|
+
is_staff: z.boolean(),
|
|
1188
|
+
// Optional fields that may appear in some responses
|
|
1189
|
+
email: z.string().email().optional(),
|
|
1190
|
+
num_collection: z.number().optional(),
|
|
1191
|
+
num_wantlist: z.number().optional(),
|
|
1192
|
+
num_unread: z.number().optional()
|
|
1193
|
+
});
|
|
1194
|
+
var UserProfileEditInputSchema = z.object({
|
|
1195
|
+
...UsernameInputSchema.shape,
|
|
1196
|
+
name: z.string().optional(),
|
|
1197
|
+
home_page: urlOrEmptySchema().optional(),
|
|
1198
|
+
location: z.string().optional(),
|
|
1199
|
+
profile: z.string().optional(),
|
|
1200
|
+
curr_abbr: CurrencyCodeSchema.optional()
|
|
1201
|
+
});
|
|
1202
|
+
var UserWantlistParamsSchema = UsernameInputSchema.merge(
|
|
1203
|
+
QueryParamsSchema(["added", "artist", "label", "rating", "title", "year"])
|
|
1204
|
+
);
|
|
1205
|
+
var UserWantlistItemSchema = z.object({
|
|
1206
|
+
id: z.number(),
|
|
1207
|
+
basic_information: BasicInformationSchema,
|
|
1208
|
+
notes: z.string().optional(),
|
|
1209
|
+
rating: z.number().int().min(0).max(5).optional(),
|
|
1210
|
+
resource_url: z.string().url()
|
|
1211
|
+
});
|
|
1212
|
+
var UserWantlistSchema = PaginatedResponseSchema(UserWantlistItemSchema, "wants");
|
|
1213
|
+
var UserWantlistItemParamsSchema = UsernameInputSchema.merge(
|
|
1214
|
+
ReleaseIdParamSchema.extend({
|
|
1215
|
+
notes: z.string().optional(),
|
|
1216
|
+
rating: z.number().int().min(0).max(5).optional()
|
|
1217
|
+
})
|
|
1218
|
+
);
|
|
1219
|
+
|
|
1220
|
+
// src/services/user/collection.ts
|
|
1221
|
+
var UserCollectionService = class extends BaseUserService {
|
|
1222
|
+
/**
|
|
1223
|
+
* Add a release to a folder in a user's collection. The folder_id must be non-zero.
|
|
1224
|
+
*
|
|
1225
|
+
* @param params The parameters for the release addition
|
|
1226
|
+
* @returns {UserCollectionReleaseAdded} The added release
|
|
1227
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1228
|
+
* @throws {DiscogsPermissionError} If trying to add a release to a folder of another user
|
|
1229
|
+
* @throws {DiscogsResourceNotFoundError} If the username, folder_id, or release_id cannot be found
|
|
1230
|
+
* @throws {DiscogsValidationFailedError} If the folder_id is 0
|
|
1231
|
+
* @throws {Error} If there's an unexpected error
|
|
1232
|
+
*/
|
|
1233
|
+
async addReleaseToFolder({
|
|
1234
|
+
username,
|
|
1235
|
+
folder_id,
|
|
1236
|
+
release_id
|
|
1237
|
+
}) {
|
|
1238
|
+
try {
|
|
1239
|
+
const response = await this.request(
|
|
1240
|
+
`/${username}/collection/folders/${folder_id}/releases/${release_id}`,
|
|
1241
|
+
{
|
|
1242
|
+
method: "POST"
|
|
1243
|
+
}
|
|
1244
|
+
);
|
|
1245
|
+
const validatedResponse = UserCollectionReleaseAddedSchema.parse(response);
|
|
1246
|
+
return validatedResponse;
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
if (isDiscogsError(error)) {
|
|
1249
|
+
throw error;
|
|
1250
|
+
}
|
|
1251
|
+
throw new Error(`Failed to add release to folder: ${String(error)}`);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Create a new folder in a user's collection
|
|
1256
|
+
*
|
|
1257
|
+
* @param params The parameters for the folder creation
|
|
1258
|
+
* @returns {UserCollectionFolder} The created folder
|
|
1259
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1260
|
+
* @throws {DiscogsPermissionError} If trying to create a folder for another user
|
|
1261
|
+
* @throws {DiscogsResourceNotFoundError} If the username cannot be found
|
|
1262
|
+
* @throws {Error} If there's an unexpected error
|
|
1263
|
+
*/
|
|
1264
|
+
async createFolder({
|
|
1265
|
+
username,
|
|
1266
|
+
...body
|
|
1267
|
+
}) {
|
|
1268
|
+
try {
|
|
1269
|
+
const response = await this.request(`/${username}/collection/folders`, {
|
|
1270
|
+
method: "POST",
|
|
1271
|
+
body
|
|
1272
|
+
});
|
|
1273
|
+
const validatedResponse = UserCollectionFolderSchema.parse(response);
|
|
1274
|
+
return validatedResponse;
|
|
1275
|
+
} catch (error) {
|
|
1276
|
+
if (isDiscogsError(error)) {
|
|
1277
|
+
throw error;
|
|
1278
|
+
}
|
|
1279
|
+
throw new Error(`Failed to create folder: ${String(error)}`);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Delete a folder from a user's collection. A folder must be empty before it can be deleted.
|
|
1284
|
+
*
|
|
1285
|
+
* @param params The parameters for the folder deletion
|
|
1286
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1287
|
+
* @throws {DiscogsPermissionError} If trying to delete a folder of another user
|
|
1288
|
+
* @throws {DiscogsResourceNotFoundError} If the username or folder cannot be found
|
|
1289
|
+
* @throws {DiscogsValidationFailedError} If the folder is not empty
|
|
1290
|
+
* @throws {Error} If there's an unexpected error
|
|
1291
|
+
*/
|
|
1292
|
+
async deleteFolder({ username, folder_id }) {
|
|
1293
|
+
try {
|
|
1294
|
+
await this.request(`/${username}/collection/folders/${folder_id}`, {
|
|
1295
|
+
method: "DELETE"
|
|
1296
|
+
});
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
if (isDiscogsError(error)) {
|
|
1299
|
+
throw error;
|
|
1300
|
+
}
|
|
1301
|
+
throw new Error(`Failed to delete folder: ${String(error)}`);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* Remove an instance of a release from a user's collection folder
|
|
1306
|
+
*
|
|
1307
|
+
* @param params The parameters for the release deletion
|
|
1308
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1309
|
+
* @throws {DiscogsPermissionError} If trying to delete a release from a folder of another user
|
|
1310
|
+
* @throws {DiscogsResourceNotFoundError} If the username, folder_id, release_id, or instance_id cannot be found
|
|
1311
|
+
* @throws {DiscogsValidationFailedError} If the folder_id is 0
|
|
1312
|
+
* @throws {Error} If there's an unexpected error
|
|
1313
|
+
*/
|
|
1314
|
+
async deleteReleaseFromFolder({
|
|
1315
|
+
username,
|
|
1316
|
+
folder_id,
|
|
1317
|
+
release_id,
|
|
1318
|
+
instance_id
|
|
1319
|
+
}) {
|
|
1320
|
+
try {
|
|
1321
|
+
await this.request(
|
|
1322
|
+
`/${username}/collection/folders/${folder_id}/releases/${release_id}/instances/${instance_id}`,
|
|
1323
|
+
{
|
|
1324
|
+
method: "DELETE"
|
|
1325
|
+
}
|
|
1326
|
+
);
|
|
1327
|
+
} catch (error) {
|
|
1328
|
+
if (isDiscogsError(error)) {
|
|
1329
|
+
throw error;
|
|
1330
|
+
}
|
|
1331
|
+
throw new Error(`Failed to delete release from folder: ${String(error)}`);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Edit a folder's metadata. Folders 0 and 1 cannot be renamed.
|
|
1336
|
+
*
|
|
1337
|
+
* @param params The parameters for the folder edit
|
|
1338
|
+
* @returns {UserCollectionFolder} The edited folder
|
|
1339
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1340
|
+
* @throws {DiscogsPermissionError} If trying to edit a folder of another user
|
|
1341
|
+
* @throws {DiscogsResourceNotFoundError} If the username or folder cannot be found
|
|
1342
|
+
* @throws {DiscogsValidationFailedError} If the folder_id is 0 or 1
|
|
1343
|
+
* @throws {Error} If there's an unexpected error
|
|
1344
|
+
*/
|
|
1345
|
+
async editFolder({
|
|
1346
|
+
username,
|
|
1347
|
+
folder_id,
|
|
1348
|
+
...body
|
|
1349
|
+
}) {
|
|
1350
|
+
try {
|
|
1351
|
+
const response = await this.request(
|
|
1352
|
+
`/${username}/collection/folders/${folder_id}`,
|
|
1353
|
+
{
|
|
1354
|
+
method: "POST",
|
|
1355
|
+
body
|
|
1356
|
+
}
|
|
1357
|
+
);
|
|
1358
|
+
const validatedResponse = UserCollectionFolderSchema.parse(response);
|
|
1359
|
+
return validatedResponse;
|
|
1360
|
+
} catch (error) {
|
|
1361
|
+
if (isDiscogsError(error)) {
|
|
1362
|
+
throw error;
|
|
1363
|
+
}
|
|
1364
|
+
throw new Error(`Failed to edit folder: ${String(error)}`);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Find a release in a user's collection
|
|
1369
|
+
*
|
|
1370
|
+
* @param params The parameters for the release search
|
|
1371
|
+
* @returns {UserCollectionItemsByRelease} The releases in the user's collection
|
|
1372
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1373
|
+
* @throws {DiscogsPermissionError} If trying to search a private collection
|
|
1374
|
+
* @throws {DiscogsResourceNotFoundError} If the username cannot be found
|
|
1375
|
+
* @throws {Error} If there's an unexpected error
|
|
1376
|
+
*/
|
|
1377
|
+
async findRelease({
|
|
1378
|
+
username,
|
|
1379
|
+
release_id,
|
|
1380
|
+
...options
|
|
1381
|
+
}) {
|
|
1382
|
+
try {
|
|
1383
|
+
const response = await this.request(
|
|
1384
|
+
`/${username}/collection/releases/${release_id}`,
|
|
1385
|
+
{
|
|
1386
|
+
params: options
|
|
1387
|
+
}
|
|
1388
|
+
);
|
|
1389
|
+
const validatedResponse = UserCollectionItemsByReleaseSchema.parse(response);
|
|
1390
|
+
return validatedResponse;
|
|
1391
|
+
} catch (error) {
|
|
1392
|
+
if (isDiscogsError(error)) {
|
|
1393
|
+
throw error;
|
|
1394
|
+
}
|
|
1395
|
+
throw new Error(`Failed to find release in collection: ${String(error)}`);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Retrieve a list of user-defined collection notes fields. These fields are available on every release in the collection.
|
|
1400
|
+
*
|
|
1401
|
+
* @param username The username of whose collection custom fields you are requesting
|
|
1402
|
+
* @returns {UserCollectionCustomFields} The user's collection custom fields
|
|
1403
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1404
|
+
* @throws {DiscogsPermissionError} If trying to get custom fields of a private collection
|
|
1405
|
+
* @throws {DiscogsResourceNotFoundError} If the username cannot be found
|
|
1406
|
+
* @throws {Error} If there's an unexpected error
|
|
1407
|
+
*/
|
|
1408
|
+
async getCustomFields({ username }) {
|
|
1409
|
+
try {
|
|
1410
|
+
const response = await this.request(
|
|
1411
|
+
`/${username}/collection/fields`
|
|
1412
|
+
);
|
|
1413
|
+
const validatedResponse = UserCollectionCustomFieldsSchema.parse(response);
|
|
1414
|
+
return validatedResponse;
|
|
1415
|
+
} catch (error) {
|
|
1416
|
+
if (isDiscogsError(error)) {
|
|
1417
|
+
throw error;
|
|
1418
|
+
}
|
|
1419
|
+
throw new Error(`Failed to get collection custom fields: ${String(error)}`);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Retrieve a list of folders in a user's collection
|
|
1424
|
+
*
|
|
1425
|
+
* @param username The username of whose collection folders you are requesting
|
|
1426
|
+
* @returns {UserCollectionFolder[]} The user's collection folders
|
|
1427
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1428
|
+
* @throws {DiscogsPermissionError} If another user's collection does not have public folders
|
|
1429
|
+
* @throws {DiscogsResourceNotFoundError} If the username cannot be found
|
|
1430
|
+
* @throws {Error} If there's an unexpected error
|
|
1431
|
+
*/
|
|
1432
|
+
async getFolders({ username }) {
|
|
1433
|
+
try {
|
|
1434
|
+
const response = await this.request(`/${username}/collection/folders`);
|
|
1435
|
+
const validatedResponse = UserCollectionFoldersSchema.parse(response);
|
|
1436
|
+
return validatedResponse;
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
if (isDiscogsError(error)) {
|
|
1439
|
+
throw error;
|
|
1440
|
+
}
|
|
1441
|
+
throw new Error(`Failed to get collection folders: ${String(error)}`);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Retrieve metadata about a folder in a user's collection
|
|
1446
|
+
*
|
|
1447
|
+
* @param params The parameters for the folder retrieval
|
|
1448
|
+
* @returns {UserCollectionFolder} The retrieved folder
|
|
1449
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1450
|
+
* @throws {DiscogsPermissionError} If trying to get a private folder of another user
|
|
1451
|
+
* @throws {DiscogsResourceNotFoundError} If the username or folder cannot be found
|
|
1452
|
+
* @throws {Error} If there's an unexpected error
|
|
1453
|
+
*/
|
|
1454
|
+
async getFolder({
|
|
1455
|
+
username,
|
|
1456
|
+
folder_id
|
|
1457
|
+
}) {
|
|
1458
|
+
try {
|
|
1459
|
+
const response = await this.request(
|
|
1460
|
+
`/${username}/collection/folders/${folder_id}`
|
|
1461
|
+
);
|
|
1462
|
+
const validatedResponse = UserCollectionFolderSchema.parse(response);
|
|
1463
|
+
return validatedResponse;
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
if (isDiscogsError(error)) {
|
|
1466
|
+
throw error;
|
|
1467
|
+
}
|
|
1468
|
+
throw new Error(`Failed to get folder: ${String(error)}`);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Returns the list of item in a folder in a user's collection
|
|
1473
|
+
*
|
|
1474
|
+
* @param params The parameters for the item retrieval
|
|
1475
|
+
* @returns {UserCollectionItemsByRelease} The items in the user's collection
|
|
1476
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1477
|
+
* @throws {DiscogsPermissionError} If trying to get a private collection
|
|
1478
|
+
* @throws {DiscogsResourceNotFoundError} If the username or folder cannot be found
|
|
1479
|
+
* @throws {Error} If there's an unexpected error
|
|
1480
|
+
*/
|
|
1481
|
+
async getItems({
|
|
1482
|
+
username,
|
|
1483
|
+
folder_id,
|
|
1484
|
+
...options
|
|
1485
|
+
}) {
|
|
1486
|
+
try {
|
|
1487
|
+
const response = await this.request(
|
|
1488
|
+
`/${username}/collection/folders/${folder_id}/releases`,
|
|
1489
|
+
{
|
|
1490
|
+
params: options
|
|
1491
|
+
}
|
|
1492
|
+
);
|
|
1493
|
+
const validatedResponse = UserCollectionItemsByReleaseSchema.parse(response);
|
|
1494
|
+
return validatedResponse;
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
if (isDiscogsError(error)) {
|
|
1497
|
+
throw error;
|
|
1498
|
+
}
|
|
1499
|
+
throw new Error(`Failed to get collection items: ${String(error)}`);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Returns the minimum, median, and maximum value of a user's collection
|
|
1504
|
+
*
|
|
1505
|
+
* @param username The username of whose collection value you are requesting
|
|
1506
|
+
* @returns {UserCollectionValue} The user's collection value
|
|
1507
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1508
|
+
* @throws {DiscogsPermissionError} If trying to get the collection value of another user
|
|
1509
|
+
* @throws {DiscogsResourceNotFoundError} If the username cannot be found
|
|
1510
|
+
* @throws {Error} If there's an unexpected error
|
|
1511
|
+
*/
|
|
1512
|
+
async getValue({ username }) {
|
|
1513
|
+
try {
|
|
1514
|
+
const response = await this.request(`/${username}/collection/value`);
|
|
1515
|
+
const validatedResponse = UserCollectionValueSchema.parse(response);
|
|
1516
|
+
return validatedResponse;
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
if (isDiscogsError(error)) {
|
|
1519
|
+
throw error;
|
|
1520
|
+
}
|
|
1521
|
+
throw new Error(`Failed to get collection value: ${String(error)}`);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Move a release in a user's collection to another folder
|
|
1526
|
+
*
|
|
1527
|
+
* @param params The parameters for the release move
|
|
1528
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1529
|
+
* @throws {DiscogsPermissionError} If trying to move a collection release of another user
|
|
1530
|
+
* @throws {DiscogsResourceNotFoundError} If the user, source folder, destination folder, release, or instance cannot be found
|
|
1531
|
+
* @throws {Error} If there's an unexpected error
|
|
1532
|
+
*/
|
|
1533
|
+
async moveRelease({
|
|
1534
|
+
username,
|
|
1535
|
+
folder_id,
|
|
1536
|
+
release_id,
|
|
1537
|
+
instance_id,
|
|
1538
|
+
...body
|
|
1539
|
+
}) {
|
|
1540
|
+
try {
|
|
1541
|
+
await this.request(
|
|
1542
|
+
`/${username}/collection/folders/${folder_id}/releases/${release_id}/instances/${instance_id}`,
|
|
1543
|
+
{
|
|
1544
|
+
method: "POST",
|
|
1545
|
+
body: { folder_id: body.destination_folder_id }
|
|
1546
|
+
}
|
|
1547
|
+
);
|
|
1548
|
+
} catch (error) {
|
|
1549
|
+
if (isDiscogsError(error)) {
|
|
1550
|
+
throw error;
|
|
1551
|
+
}
|
|
1552
|
+
throw new Error(`Failed to move release: ${String(error)}`);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Rate a release in a user's collection
|
|
1557
|
+
*
|
|
1558
|
+
* @param params The parameters for the release rating
|
|
1559
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1560
|
+
* @throws {DiscogsPermissionError} If trying to rate a collection release of another user
|
|
1561
|
+
* @throws {DiscogsResourceNotFoundError} If the username, folder_id, release_id, or instance_id cannot be found
|
|
1562
|
+
* @throws {Error} If there's an unexpected error
|
|
1563
|
+
*/
|
|
1564
|
+
async rateRelease({
|
|
1565
|
+
username,
|
|
1566
|
+
folder_id,
|
|
1567
|
+
release_id,
|
|
1568
|
+
instance_id,
|
|
1569
|
+
...body
|
|
1570
|
+
}) {
|
|
1571
|
+
try {
|
|
1572
|
+
await this.request(
|
|
1573
|
+
`/${username}/collection/folders/${folder_id}/releases/${release_id}/instances/${instance_id}`,
|
|
1574
|
+
{
|
|
1575
|
+
method: "POST",
|
|
1576
|
+
body
|
|
1577
|
+
}
|
|
1578
|
+
);
|
|
1579
|
+
} catch (error) {
|
|
1580
|
+
if (isDiscogsError(error)) {
|
|
1581
|
+
throw error;
|
|
1582
|
+
}
|
|
1583
|
+
throw new Error(`Failed to rate release: ${String(error)}`);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
|
|
1588
|
+
// src/services/user/lists.ts
|
|
1589
|
+
var UserListsService = class extends BaseUserService {
|
|
1590
|
+
/**
|
|
1591
|
+
* Get a user's lists
|
|
1592
|
+
* @param params - Parameters for the request including username and optional pagination/sorting
|
|
1593
|
+
* @returns {UserLists} A paginated response containing the user's lists
|
|
1594
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1595
|
+
* @throws {DiscogsResourceNotFoundError} If the username cannot be found
|
|
1596
|
+
* @throws {Error} If there's a validation error or other unexpected error
|
|
1597
|
+
*/
|
|
1598
|
+
async get({ username, ...options }) {
|
|
1599
|
+
try {
|
|
1600
|
+
const response = await this.request(`/${username}/lists`, {
|
|
1601
|
+
params: options
|
|
1602
|
+
});
|
|
1603
|
+
const validatedResponse = UserListsSchema.parse(response);
|
|
1604
|
+
return validatedResponse;
|
|
1605
|
+
} catch (error) {
|
|
1606
|
+
if (isDiscogsError(error)) {
|
|
1607
|
+
throw error;
|
|
1608
|
+
}
|
|
1609
|
+
throw new Error(`Failed to get lists: ${String(error)}`);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
|
|
1614
|
+
// src/services/user/profile.ts
|
|
1615
|
+
var UserProfileService = class extends BaseUserService {
|
|
1616
|
+
/**
|
|
1617
|
+
* Retrieve a user by username
|
|
1618
|
+
*
|
|
1619
|
+
* @param username The username of whose profile you are requesting
|
|
1620
|
+
* @returns {UserProfile} The user's profile information
|
|
1621
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1622
|
+
* @throws {DiscogsResourceNotFoundError} If the username cannot be found
|
|
1623
|
+
* @throws {Error} If there's a validation error or other unexpected error
|
|
1624
|
+
*/
|
|
1625
|
+
async get({ username }) {
|
|
1626
|
+
try {
|
|
1627
|
+
const response = await this.request(`/${username}`);
|
|
1628
|
+
const validatedResponse = UserProfileSchema.parse(response);
|
|
1629
|
+
return validatedResponse;
|
|
1630
|
+
} catch (error) {
|
|
1631
|
+
if (isDiscogsError(error)) {
|
|
1632
|
+
throw error;
|
|
1633
|
+
}
|
|
1634
|
+
throw new Error(`Failed to get profile: ${String(error)}`);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Edit a user's profile data
|
|
1639
|
+
*
|
|
1640
|
+
* @param params UserProfileEditInput
|
|
1641
|
+
* @returns {UserProfile} The user's profile information
|
|
1642
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1643
|
+
* @throws {DiscogsPermissionError} If trying to edit a profile that is not the authenticated user
|
|
1644
|
+
* @throws {DiscogsResourceNotFoundError} If the username cannot be found
|
|
1645
|
+
* @throws {Error} If there's a validation error or other unexpected error
|
|
1646
|
+
*/
|
|
1647
|
+
async edit({ username, ...body }) {
|
|
1648
|
+
try {
|
|
1649
|
+
const response = await this.request(`/${username}`, {
|
|
1650
|
+
method: "POST",
|
|
1651
|
+
body
|
|
1652
|
+
});
|
|
1653
|
+
const validatedResponse = UserProfileSchema.parse(response);
|
|
1654
|
+
return validatedResponse;
|
|
1655
|
+
} catch (error) {
|
|
1656
|
+
if (isDiscogsError(error)) {
|
|
1657
|
+
throw error;
|
|
1658
|
+
}
|
|
1659
|
+
throw new Error(`Failed to edit profile: ${String(error)}`);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
// src/services/user/wants.ts
|
|
1665
|
+
var UserWantsService = class extends BaseUserService {
|
|
1666
|
+
/**
|
|
1667
|
+
* Returns the list of releases in a user's wantlist
|
|
1668
|
+
*
|
|
1669
|
+
* @param params - Parameters for the wantlist request including username and optional pagination/sorting
|
|
1670
|
+
* @returns {UserWantlist} The user's wantlist with pagination metadata
|
|
1671
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1672
|
+
* @throws {DiscogsPermissionError} If trying to get the private wantlist of another user
|
|
1673
|
+
* @throws {DiscogsResourceNotFoundError} If the username cannot be found
|
|
1674
|
+
* @throws {Error} If there's a validation error or other unexpected error
|
|
1675
|
+
*/
|
|
1676
|
+
async getList({ username, ...options }) {
|
|
1677
|
+
try {
|
|
1678
|
+
const response = await this.request(`/${username}/wants`, {
|
|
1679
|
+
params: options
|
|
1680
|
+
});
|
|
1681
|
+
const validatedResponse = UserWantlistSchema.parse(response);
|
|
1682
|
+
return validatedResponse;
|
|
1683
|
+
} catch (error) {
|
|
1684
|
+
if (isDiscogsError(error)) {
|
|
1685
|
+
throw error;
|
|
1686
|
+
}
|
|
1687
|
+
throw new Error(`Failed to get wantlist: ${String(error)}`);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Add a release to a user's wantlist
|
|
1692
|
+
*
|
|
1693
|
+
* @param params - Parameters for adding a release to wantlist
|
|
1694
|
+
* @returns {UserWantlistItem} The added wantlist item
|
|
1695
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1696
|
+
* @throws {DiscogsPermissionError} If trying to add to another user's wantlist
|
|
1697
|
+
* @throws {DiscogsResourceNotFoundError} If the username or release_id cannot be found
|
|
1698
|
+
* @throws {Error} If there's a validation error or other unexpected error
|
|
1699
|
+
*/
|
|
1700
|
+
async addItem({
|
|
1701
|
+
username,
|
|
1702
|
+
release_id,
|
|
1703
|
+
...body
|
|
1704
|
+
}) {
|
|
1705
|
+
try {
|
|
1706
|
+
const response = await this.request(`/${username}/wants/${release_id}`, {
|
|
1707
|
+
method: "PUT",
|
|
1708
|
+
body
|
|
1709
|
+
});
|
|
1710
|
+
const validatedResponse = UserWantlistItemSchema.parse(response);
|
|
1711
|
+
return validatedResponse;
|
|
1712
|
+
} catch (error) {
|
|
1713
|
+
if (isDiscogsError(error)) {
|
|
1714
|
+
throw error;
|
|
1715
|
+
}
|
|
1716
|
+
throw new Error(`Failed to add to wantlist: ${String(error)}`);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Edit a release in a user's wantlist
|
|
1721
|
+
*
|
|
1722
|
+
* @param params - Parameters for editing a release in a wantlist
|
|
1723
|
+
* @returns {UserWantlistItem} The edited wantlist item
|
|
1724
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1725
|
+
* @throws {DiscogsPermissionError} If trying to edit a release in another user's wantlist
|
|
1726
|
+
* @throws {DiscogsResourceNotFoundError} If the username or release_id cannot be found
|
|
1727
|
+
* @throws {Error} If there's a validation error or other unexpected error
|
|
1728
|
+
*/
|
|
1729
|
+
async editItem({
|
|
1730
|
+
username,
|
|
1731
|
+
release_id,
|
|
1732
|
+
...body
|
|
1733
|
+
}) {
|
|
1734
|
+
try {
|
|
1735
|
+
const response = await this.request(`/${username}/wants/${release_id}`, {
|
|
1736
|
+
method: "POST",
|
|
1737
|
+
body
|
|
1738
|
+
});
|
|
1739
|
+
const validatedResponse = UserWantlistItemSchema.parse(response);
|
|
1740
|
+
return validatedResponse;
|
|
1741
|
+
} catch (error) {
|
|
1742
|
+
if (isDiscogsError(error)) {
|
|
1743
|
+
throw error;
|
|
1744
|
+
}
|
|
1745
|
+
throw new Error(`Failed to add to wantlist: ${String(error)}`);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Delete a release from a user's wantlist
|
|
1750
|
+
*
|
|
1751
|
+
* @param params - Parameters for deleting a release from wantlist including username and release_id
|
|
1752
|
+
* @throws {DiscogsAuthenticationError} If authentication fails
|
|
1753
|
+
* @throws {DiscogsPermissionError} If trying to delete from another user's wantlist
|
|
1754
|
+
* @throws {DiscogsResourceNotFoundError} If the username or release_id cannot be found
|
|
1755
|
+
* @throws {Error} If there's an unexpected error
|
|
1756
|
+
*/
|
|
1757
|
+
async deleteItem({ username, release_id }) {
|
|
1758
|
+
try {
|
|
1759
|
+
await this.request(`/${username}/wants/${release_id}`, {
|
|
1760
|
+
method: "DELETE"
|
|
1761
|
+
});
|
|
1762
|
+
} catch (error) {
|
|
1763
|
+
if (isDiscogsError(error)) {
|
|
1764
|
+
throw error;
|
|
1765
|
+
}
|
|
1766
|
+
throw new Error(`Failed to delete from wantlist: ${String(error)}`);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
|
|
1771
|
+
// src/services/user/index.ts
|
|
1772
|
+
var UserService = class {
|
|
1773
|
+
collection;
|
|
1774
|
+
lists;
|
|
1775
|
+
profile;
|
|
1776
|
+
wants;
|
|
1777
|
+
constructor() {
|
|
1778
|
+
this.collection = new UserCollectionService();
|
|
1779
|
+
this.lists = new UserListsService();
|
|
1780
|
+
this.profile = new UserProfileService();
|
|
1781
|
+
this.wants = new UserWantsService();
|
|
1782
|
+
}
|
|
1783
|
+
};
|
|
1784
|
+
|
|
1785
|
+
// src/tools/userCollection.ts
|
|
1786
|
+
var addReleaseToUserCollectionFolderTool = {
|
|
1787
|
+
name: "add_release_to_user_collection_folder",
|
|
1788
|
+
description: `Add a release to a folder in a user's collection. The folder_id must be non-zero.`,
|
|
1789
|
+
parameters: UserCollectionFolderReleaseParamsSchema,
|
|
1790
|
+
execute: async (args) => {
|
|
1791
|
+
try {
|
|
1792
|
+
const userService = new UserService();
|
|
1793
|
+
const release = await userService.collection.addReleaseToFolder(args);
|
|
1794
|
+
return JSON.stringify(release);
|
|
1795
|
+
} catch (error) {
|
|
1796
|
+
throw formatDiscogsError(error);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
var createUserCollectionFolderTool = {
|
|
1801
|
+
name: "create_user_collection_folder",
|
|
1802
|
+
description: `Create a new folder in a user's collection`,
|
|
1803
|
+
parameters: UserCollectionFolderCreateParamsSchema,
|
|
1804
|
+
execute: async (args) => {
|
|
1805
|
+
try {
|
|
1806
|
+
const userService = new UserService();
|
|
1807
|
+
const folder = await userService.collection.createFolder(args);
|
|
1808
|
+
return JSON.stringify(folder);
|
|
1809
|
+
} catch (error) {
|
|
1810
|
+
throw formatDiscogsError(error);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
var deleteReleaseFromUserCollectionFolderTool = {
|
|
1815
|
+
name: "delete_release_from_user_collection_folder",
|
|
1816
|
+
description: `Remove an instance of a release from a user's collection folder. The folder_id must be non-zero.`,
|
|
1817
|
+
parameters: UserCollectionReleaseDeletedParamsSchema,
|
|
1818
|
+
execute: async (args) => {
|
|
1819
|
+
try {
|
|
1820
|
+
const userService = new UserService();
|
|
1821
|
+
await userService.collection.deleteReleaseFromFolder(args);
|
|
1822
|
+
return "Release deleted successfully";
|
|
1823
|
+
} catch (error) {
|
|
1824
|
+
throw formatDiscogsError(error);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1828
|
+
var deleteUserCollectionFolderTool = {
|
|
1829
|
+
name: "delete_user_collection_folder",
|
|
1830
|
+
description: `Delete a folder from a user's collection. A folder must be empty before it can be deleted.`,
|
|
1831
|
+
parameters: UserCollectionFolderParamsSchema,
|
|
1832
|
+
execute: async (args) => {
|
|
1833
|
+
try {
|
|
1834
|
+
const userService = new UserService();
|
|
1835
|
+
await userService.collection.deleteFolder(args);
|
|
1836
|
+
return "Folder deleted successfully";
|
|
1837
|
+
} catch (error) {
|
|
1838
|
+
throw formatDiscogsError(error);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
var editUserCollectionFolderTool = {
|
|
1843
|
+
name: "edit_user_collection_folder",
|
|
1844
|
+
description: `Edit a folder's metadata. Folders 0 and 1 cannot be renamed.`,
|
|
1845
|
+
parameters: UserCollectionFolderEditParamsSchema,
|
|
1846
|
+
execute: async (args) => {
|
|
1847
|
+
try {
|
|
1848
|
+
const userService = new UserService();
|
|
1849
|
+
const folder = await userService.collection.editFolder(args);
|
|
1850
|
+
return JSON.stringify(folder);
|
|
1851
|
+
} catch (error) {
|
|
1852
|
+
throw formatDiscogsError(error);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
var findReleaseInUserCollectionTool = {
|
|
1857
|
+
name: "find_release_in_user_collection",
|
|
1858
|
+
description: `Find a release in a user's collection`,
|
|
1859
|
+
parameters: UserCollectionReleaseParamsSchema,
|
|
1860
|
+
execute: async (args) => {
|
|
1861
|
+
try {
|
|
1862
|
+
const userService = new UserService();
|
|
1863
|
+
const releases = await userService.collection.findRelease(args);
|
|
1864
|
+
return JSON.stringify(releases);
|
|
1865
|
+
} catch (error) {
|
|
1866
|
+
throw formatDiscogsError(error);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
var getUserCollectionCustomFieldsTool = {
|
|
1871
|
+
name: "get_user_collection_custom_fields",
|
|
1872
|
+
description: `Retrieve a list of user-defined collection notes fields. These fields are available on every release in the collection.`,
|
|
1873
|
+
parameters: UsernameInputSchema,
|
|
1874
|
+
execute: async (args) => {
|
|
1875
|
+
try {
|
|
1876
|
+
const userService = new UserService();
|
|
1877
|
+
const customFields = await userService.collection.getCustomFields(args);
|
|
1878
|
+
return JSON.stringify(customFields);
|
|
1879
|
+
} catch (error) {
|
|
1880
|
+
throw formatDiscogsError(error);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1884
|
+
var getUserCollectionFolderTool = {
|
|
1885
|
+
name: "get_user_collection_folder",
|
|
1886
|
+
description: `Retrieve metadata about a folder in a user's collection`,
|
|
1887
|
+
parameters: UserCollectionFolderParamsSchema,
|
|
1888
|
+
execute: async (args) => {
|
|
1889
|
+
try {
|
|
1890
|
+
const userService = new UserService();
|
|
1891
|
+
const folder = await userService.collection.getFolder(args);
|
|
1892
|
+
return JSON.stringify(folder);
|
|
1893
|
+
} catch (error) {
|
|
1894
|
+
throw formatDiscogsError(error);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
var getUserCollectionFoldersTool = {
|
|
1899
|
+
name: "get_user_collection_folders",
|
|
1900
|
+
description: `Retrieve a list of folders in a user's collection`,
|
|
1901
|
+
parameters: UsernameInputSchema,
|
|
1902
|
+
execute: async (args) => {
|
|
1903
|
+
try {
|
|
1904
|
+
const userService = new UserService();
|
|
1905
|
+
const collectionFolders = await userService.collection.getFolders(args);
|
|
1906
|
+
return JSON.stringify(collectionFolders);
|
|
1907
|
+
} catch (error) {
|
|
1908
|
+
throw formatDiscogsError(error);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
var getUserCollectionItemsTool = {
|
|
1913
|
+
name: "get_user_collection_items",
|
|
1914
|
+
description: `Retrieve a list of items in a user's collection`,
|
|
1915
|
+
parameters: UserCollectionItemsParamsSchema,
|
|
1916
|
+
execute: async (args) => {
|
|
1917
|
+
try {
|
|
1918
|
+
const userService = new UserService();
|
|
1919
|
+
const items = await userService.collection.getItems(args);
|
|
1920
|
+
return JSON.stringify(items);
|
|
1921
|
+
} catch (error) {
|
|
1922
|
+
throw formatDiscogsError(error);
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
1926
|
+
var getUserCollectionValueTool = {
|
|
1927
|
+
name: "get_user_collection_value",
|
|
1928
|
+
description: `Returns the minimum, median, and maximum value of a user's collection`,
|
|
1929
|
+
parameters: UsernameInputSchema,
|
|
1930
|
+
execute: async (args) => {
|
|
1931
|
+
try {
|
|
1932
|
+
const userService = new UserService();
|
|
1933
|
+
const collectionValue = await userService.collection.getValue(args);
|
|
1934
|
+
return JSON.stringify(collectionValue);
|
|
1935
|
+
} catch (error) {
|
|
1936
|
+
throw formatDiscogsError(error);
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
};
|
|
1940
|
+
var moveReleaseInUserCollectionTool = {
|
|
1941
|
+
name: "move_release_in_user_collection",
|
|
1942
|
+
description: `Move a release in a user's collection to another folder`,
|
|
1943
|
+
parameters: UserCollectionMoveReleaseParamsSchema,
|
|
1944
|
+
execute: async (args) => {
|
|
1945
|
+
try {
|
|
1946
|
+
const userService = new UserService();
|
|
1947
|
+
await userService.collection.moveRelease(args);
|
|
1948
|
+
return "Release moved successfully";
|
|
1949
|
+
} catch (error) {
|
|
1950
|
+
throw formatDiscogsError(error);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1954
|
+
var rateReleaseInUserCollectionTool = {
|
|
1955
|
+
name: "rate_release_in_user_collection",
|
|
1956
|
+
description: `Rate a release in a user's collection. The folder_id must be non-zero.`,
|
|
1957
|
+
parameters: UserCollectionReleaseRatingParamsSchema,
|
|
1958
|
+
execute: async (args) => {
|
|
1959
|
+
try {
|
|
1960
|
+
const userService = new UserService();
|
|
1961
|
+
await userService.collection.rateRelease(args);
|
|
1962
|
+
return "Release rated successfully";
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
throw formatDiscogsError(error);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
};
|
|
1968
|
+
function registerUserCollectionTools(server) {
|
|
1969
|
+
server.addTool(getUserCollectionFoldersTool);
|
|
1970
|
+
server.addTool(createUserCollectionFolderTool);
|
|
1971
|
+
server.addTool(getUserCollectionFolderTool);
|
|
1972
|
+
server.addTool(editUserCollectionFolderTool);
|
|
1973
|
+
server.addTool(deleteUserCollectionFolderTool);
|
|
1974
|
+
server.addTool(findReleaseInUserCollectionTool);
|
|
1975
|
+
server.addTool(getUserCollectionItemsTool);
|
|
1976
|
+
server.addTool(addReleaseToUserCollectionFolderTool);
|
|
1977
|
+
server.addTool(rateReleaseInUserCollectionTool);
|
|
1978
|
+
server.addTool(moveReleaseInUserCollectionTool);
|
|
1979
|
+
server.addTool(deleteReleaseFromUserCollectionFolderTool);
|
|
1980
|
+
server.addTool(getUserCollectionCustomFieldsTool);
|
|
1981
|
+
server.addTool(getUserCollectionValueTool);
|
|
1982
|
+
}
|
|
1983
|
+
var DiscogsUserIdentitySchema = z.object({
|
|
1984
|
+
id: z.number(),
|
|
1985
|
+
username: z.string(),
|
|
1986
|
+
resource_url: z.string().url(),
|
|
1987
|
+
consumer_name: z.string()
|
|
1988
|
+
});
|
|
1989
|
+
|
|
1990
|
+
// src/services/oauth.ts
|
|
1991
|
+
var OAuthService = class extends DiscogsService {
|
|
1992
|
+
constructor() {
|
|
1993
|
+
super("/oauth");
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Get the identity of the authenticated user
|
|
1997
|
+
* @returns The user's identity information
|
|
1998
|
+
* @throws {DiscogsAuthenticationError} If authentication fails (401)
|
|
1999
|
+
*/
|
|
2000
|
+
async getUserIdentity() {
|
|
2001
|
+
try {
|
|
2002
|
+
const response = await this.request("/identity");
|
|
2003
|
+
const validatedResponse = DiscogsUserIdentitySchema.parse(response);
|
|
2004
|
+
return validatedResponse;
|
|
2005
|
+
} catch (error) {
|
|
2006
|
+
if (isDiscogsError(error)) {
|
|
2007
|
+
throw error;
|
|
2008
|
+
}
|
|
2009
|
+
throw new Error(`Failed to get identity: ${String(error)}`);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
|
|
2014
|
+
// src/tools/userIdentity.ts
|
|
2015
|
+
var getUserIdentityTool = {
|
|
2016
|
+
name: "get_user_identity",
|
|
2017
|
+
description: "Retrieve basic information about the authenticated user",
|
|
2018
|
+
parameters: z.object({}),
|
|
2019
|
+
execute: async () => {
|
|
2020
|
+
try {
|
|
2021
|
+
const oauthService = new OAuthService();
|
|
2022
|
+
const identity = await oauthService.getUserIdentity();
|
|
2023
|
+
return JSON.stringify(identity);
|
|
2024
|
+
} catch (error) {
|
|
2025
|
+
throw formatDiscogsError(error);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
var getUserProfileTool = {
|
|
2030
|
+
name: "get_user_profile",
|
|
2031
|
+
description: "Retrieve a user by username",
|
|
2032
|
+
parameters: UsernameInputSchema,
|
|
2033
|
+
execute: async (args) => {
|
|
2034
|
+
try {
|
|
2035
|
+
const userService = new UserService();
|
|
2036
|
+
const profile = await userService.profile.get(args);
|
|
2037
|
+
return JSON.stringify(profile);
|
|
2038
|
+
} catch (error) {
|
|
2039
|
+
throw formatDiscogsError(error);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
var editUserProfileTool = {
|
|
2044
|
+
name: "edit_user_profile",
|
|
2045
|
+
description: `Edit a user's profile data`,
|
|
2046
|
+
parameters: UserProfileEditInputSchema,
|
|
2047
|
+
execute: async (args) => {
|
|
2048
|
+
try {
|
|
2049
|
+
const userService = new UserService();
|
|
2050
|
+
const profile = await userService.profile.edit(args);
|
|
2051
|
+
return JSON.stringify(profile);
|
|
2052
|
+
} catch (error) {
|
|
2053
|
+
throw formatDiscogsError(error);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
};
|
|
2057
|
+
function registerUserIdentityTools(server) {
|
|
2058
|
+
server.addTool(getUserIdentityTool);
|
|
2059
|
+
server.addTool(getUserProfileTool);
|
|
2060
|
+
server.addTool(editUserProfileTool);
|
|
2061
|
+
}
|
|
2062
|
+
var ListItemSchema = z.object({
|
|
2063
|
+
id: z.number(),
|
|
2064
|
+
comment: z.string().optional(),
|
|
2065
|
+
display_title: z.string(),
|
|
2066
|
+
image_url: z.string().url(),
|
|
2067
|
+
resource_url: z.string().url(),
|
|
2068
|
+
stats: z.object({
|
|
2069
|
+
community: z.object({
|
|
2070
|
+
in_collection: z.number(),
|
|
2071
|
+
in_wantlist: z.number()
|
|
2072
|
+
}),
|
|
2073
|
+
user: z.object({
|
|
2074
|
+
in_collection: z.number(),
|
|
2075
|
+
in_wantlist: z.number()
|
|
2076
|
+
})
|
|
2077
|
+
}),
|
|
2078
|
+
type: z.string(),
|
|
2079
|
+
uri: z.string().url()
|
|
2080
|
+
});
|
|
2081
|
+
var ListSchema = z.object({
|
|
2082
|
+
id: z.number(),
|
|
2083
|
+
user: z.object({
|
|
2084
|
+
id: z.number(),
|
|
2085
|
+
avatar_url: z.string().url(),
|
|
2086
|
+
username: z.string(),
|
|
2087
|
+
resource_url: z.string().url()
|
|
2088
|
+
}),
|
|
2089
|
+
name: z.string(),
|
|
2090
|
+
description: z.string().optional(),
|
|
2091
|
+
public: z.boolean(),
|
|
2092
|
+
date_added: z.string(),
|
|
2093
|
+
date_changed: z.string(),
|
|
2094
|
+
uri: z.string().url(),
|
|
2095
|
+
resource_url: z.string().url(),
|
|
2096
|
+
image_url: z.string().url().optional(),
|
|
2097
|
+
items: z.array(ListItemSchema)
|
|
2098
|
+
});
|
|
2099
|
+
var ListIdParamSchema = z.object({
|
|
2100
|
+
list_id: z.number()
|
|
2101
|
+
});
|
|
2102
|
+
|
|
2103
|
+
// src/services/list.ts
|
|
2104
|
+
var ListService = class extends DiscogsService {
|
|
2105
|
+
constructor() {
|
|
2106
|
+
super("/lists");
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* Returns items from a specified List
|
|
2110
|
+
*
|
|
2111
|
+
* @param params - Parameters containing the list ID
|
|
2112
|
+
* @returns {List} The list information
|
|
2113
|
+
* @throws {DiscogsPermissionError} If the user doesn't have permission to access the list
|
|
2114
|
+
* @throws {DiscogsResourceNotFoundError} If the list cannot be found
|
|
2115
|
+
* @throws {Error} If there's a validation error or other unexpected error
|
|
2116
|
+
*/
|
|
2117
|
+
async getList({ list_id }) {
|
|
2118
|
+
try {
|
|
2119
|
+
const response = await this.request(`/${list_id}`);
|
|
2120
|
+
const validatedResponse = ListSchema.parse(response);
|
|
2121
|
+
return validatedResponse;
|
|
2122
|
+
} catch (error) {
|
|
2123
|
+
if (isDiscogsError(error)) {
|
|
2124
|
+
throw error;
|
|
2125
|
+
}
|
|
2126
|
+
throw new Error(`Failed to get list: ${String(error)}`);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
};
|
|
2130
|
+
|
|
2131
|
+
// src/tools/userLists.ts
|
|
2132
|
+
var getUserListsTool = {
|
|
2133
|
+
name: "get_user_lists",
|
|
2134
|
+
description: `Get a user's lists`,
|
|
2135
|
+
parameters: UserListsParamsSchema,
|
|
2136
|
+
execute: async (args) => {
|
|
2137
|
+
try {
|
|
2138
|
+
const userService = new UserService();
|
|
2139
|
+
const lists = await userService.lists.get(args);
|
|
2140
|
+
return JSON.stringify(lists);
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
throw formatDiscogsError(error);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
2146
|
+
var getListTool = {
|
|
2147
|
+
name: "get_list",
|
|
2148
|
+
description: `Get a list by ID`,
|
|
2149
|
+
parameters: ListIdParamSchema,
|
|
2150
|
+
execute: async (args) => {
|
|
2151
|
+
try {
|
|
2152
|
+
const listService = new ListService();
|
|
2153
|
+
const list = await listService.getList(args);
|
|
2154
|
+
return JSON.stringify(list);
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
throw formatDiscogsError(error);
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
function registerUserListsTools(server) {
|
|
2161
|
+
server.addTool(getUserListsTool);
|
|
2162
|
+
server.addTool(getListTool);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
// src/tools/userWantlist.ts
|
|
2166
|
+
var getUserWantlistTool = {
|
|
2167
|
+
name: "get_user_wantlist",
|
|
2168
|
+
description: `Returns the list of releases in a user's wantlist`,
|
|
2169
|
+
parameters: UserWantlistParamsSchema,
|
|
2170
|
+
execute: async (args) => {
|
|
2171
|
+
try {
|
|
2172
|
+
const userService = new UserService();
|
|
2173
|
+
const wantlist = await userService.wants.getList(args);
|
|
2174
|
+
return JSON.stringify(wantlist);
|
|
2175
|
+
} catch (error) {
|
|
2176
|
+
throw formatDiscogsError(error);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
var addToWantlistTool = {
|
|
2181
|
+
name: "add_to_wantlist",
|
|
2182
|
+
description: `Add a release to a user's wantlist`,
|
|
2183
|
+
parameters: UserWantlistItemParamsSchema,
|
|
2184
|
+
execute: async (args) => {
|
|
2185
|
+
try {
|
|
2186
|
+
const userService = new UserService();
|
|
2187
|
+
const wantlistItem = await userService.wants.addItem(args);
|
|
2188
|
+
return JSON.stringify(wantlistItem);
|
|
2189
|
+
} catch (error) {
|
|
2190
|
+
throw formatDiscogsError(error);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
};
|
|
2194
|
+
var editItemInWantlistTool = {
|
|
2195
|
+
name: "edit_item_in_wantlist",
|
|
2196
|
+
description: `Edit a release in a user's wantlist`,
|
|
2197
|
+
parameters: UserWantlistItemParamsSchema,
|
|
2198
|
+
execute: async (args) => {
|
|
2199
|
+
try {
|
|
2200
|
+
const userService = new UserService();
|
|
2201
|
+
const wantlistItem = await userService.wants.editItem(args);
|
|
2202
|
+
return JSON.stringify(wantlistItem);
|
|
2203
|
+
} catch (error) {
|
|
2204
|
+
throw formatDiscogsError(error);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
};
|
|
2208
|
+
var deleteItemInWantlistTool = {
|
|
2209
|
+
name: "delete_item_in_wantlist",
|
|
2210
|
+
description: `Delete a release from a user's wantlist`,
|
|
2211
|
+
parameters: UserWantlistItemParamsSchema,
|
|
2212
|
+
execute: async (args) => {
|
|
2213
|
+
try {
|
|
2214
|
+
const userService = new UserService();
|
|
2215
|
+
await userService.wants.deleteItem(args);
|
|
2216
|
+
return "Release deleted from wantlist";
|
|
2217
|
+
} catch (error) {
|
|
2218
|
+
throw formatDiscogsError(error);
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
};
|
|
2222
|
+
function registerUserWantlistTools(server) {
|
|
2223
|
+
server.addTool(getUserWantlistTool);
|
|
2224
|
+
server.addTool(addToWantlistTool);
|
|
2225
|
+
server.addTool(editItemInWantlistTool);
|
|
2226
|
+
server.addTool(deleteItemInWantlistTool);
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
// src/tools/index.ts
|
|
2230
|
+
function registerTools(server) {
|
|
2231
|
+
registerDatabaseTools(server);
|
|
2232
|
+
registerUserIdentityTools(server);
|
|
2233
|
+
registerUserCollectionTools(server);
|
|
2234
|
+
registerUserWantlistTools(server);
|
|
2235
|
+
registerUserListsTools(server);
|
|
2236
|
+
registerMediaTools(server);
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
// src/index.ts
|
|
2240
|
+
function assertTransportType(transportType) {
|
|
2241
|
+
return transportType === "stdio" || transportType === "sse";
|
|
2242
|
+
}
|
|
2243
|
+
try {
|
|
2244
|
+
validateConfig();
|
|
2245
|
+
const transportType = process.argv[2] ?? "stdio";
|
|
2246
|
+
if (!assertTransportType(transportType)) {
|
|
2247
|
+
throw Error(`Invalid transport type: "${transportType}". Allowed: 'stdio' (default) or 'sse'.`);
|
|
2248
|
+
}
|
|
2249
|
+
const server = new FastMCP({
|
|
2250
|
+
name: config.server.name,
|
|
2251
|
+
version: VERSION
|
|
2252
|
+
});
|
|
2253
|
+
registerTools(server);
|
|
2254
|
+
if (transportType === "stdio") {
|
|
2255
|
+
server.start({ transportType });
|
|
2256
|
+
} else if (transportType === "sse") {
|
|
2257
|
+
server.start({
|
|
2258
|
+
transportType,
|
|
2259
|
+
sse: {
|
|
2260
|
+
endpoint: "/sse",
|
|
2261
|
+
port: config.server.port
|
|
2262
|
+
}
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
log.info(`${config.server.name} started with transport type: ${transportType}`);
|
|
2266
|
+
} catch (error) {
|
|
2267
|
+
log.error(`Failed to run the ${config.server.name}: `, error);
|
|
2268
|
+
process.exit(1);
|
|
2269
|
+
}
|
|
2270
|
+
process.on("SIGINT", () => {
|
|
2271
|
+
log.info("Shutting down server...");
|
|
2272
|
+
process.exit(0);
|
|
2273
|
+
});
|
|
2274
|
+
//# sourceMappingURL=index.js.map
|
|
2275
|
+
//# sourceMappingURL=index.js.map
|