@labelgrid/mcp 0.2.2 → 0.3.1
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/CHANGELOG.md +40 -0
- package/README.md +132 -100
- package/dist/config.d.ts +15 -0
- package/dist/config.js +38 -6
- package/dist/coverage.js +79 -79
- package/dist/gating.d.ts +1 -1
- package/dist/gating.js +5 -1
- package/dist/index.js +1 -2
- package/dist/projection.d.ts +40 -0
- package/dist/projection.js +145 -0
- package/dist/resources.d.ts +29 -0
- package/dist/resources.js +107 -0
- package/dist/server.d.ts +2 -2
- package/dist/server.js +24 -4
- package/dist/tools/account.d.ts +3 -0
- package/dist/tools/account.js +32 -0
- package/dist/tools/all.js +12 -20
- package/dist/tools/catalog.d.ts +14 -0
- package/dist/tools/catalog.js +266 -0
- package/dist/tools/distribution.d.ts +13 -0
- package/dist/tools/distribution.js +265 -0
- package/dist/tools/finance.d.ts +12 -0
- package/dist/tools/finance.js +311 -0
- package/dist/tools/insights.d.ts +7 -0
- package/dist/tools/insights.js +139 -0
- package/dist/tools/reference.js +9 -24
- package/dist/tools/releases.d.ts +11 -0
- package/dist/tools/releases.js +177 -0
- package/dist/tools/setup.js +1 -1
- package/dist/tools/types.d.ts +1 -1
- package/dist/tools/webhooks.d.ts +3 -3
- package/dist/tools/webhooks.js +73 -105
- package/package.json +5 -14
- package/server.json +3 -3
- package/dist/api/content-types.d.ts +0 -33
- package/dist/api/content-types.js +0 -87
- package/dist/api/http.d.ts +0 -74
- package/dist/api/http.js +0 -392
- package/dist/api/upload.d.ts +0 -26
- package/dist/api/upload.js +0 -104
- package/dist/log.d.ts +0 -16
- package/dist/log.js +0 -35
- package/dist/tools/accounting.d.ts +0 -12
- package/dist/tools/accounting.js +0 -386
- package/dist/tools/analytics.d.ts +0 -3
- package/dist/tools/analytics.js +0 -62
- package/dist/tools/catalog-read.d.ts +0 -10
- package/dist/tools/catalog-read.js +0 -145
- package/dist/tools/catalog-write.d.ts +0 -12
- package/dist/tools/catalog-write.js +0 -206
- package/dist/tools/delivery.d.ts +0 -6
- package/dist/tools/delivery.js +0 -40
- package/dist/tools/files-read.d.ts +0 -7
- package/dist/tools/files-read.js +0 -86
- package/dist/tools/full-writes.d.ts +0 -12
- package/dist/tools/full-writes.js +0 -248
- package/dist/tools/identity.d.ts +0 -3
- package/dist/tools/identity.js +0 -28
- package/dist/tools/release-write.d.ts +0 -12
- package/dist/tools/release-write.js +0 -184
- package/dist/tools/review-read.d.ts +0 -7
- package/dist/tools/review-read.js +0 -78
package/dist/tools/accounting.js
DELETED
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Accounting toolset: statements, transactions, royalty breakdowns,
|
|
3
|
-
* artificial-streaming records, and the account summary. All read-only.
|
|
4
|
-
*
|
|
5
|
-
* Two tools (`download_statement_csv`, `download_statement_invoice`) fetch a
|
|
6
|
-
* file body. They validate the caller-supplied `save_to_path` and write ONLY
|
|
7
|
-
* there; a CSV without a save path is returned inline, truncated at 100KB.
|
|
8
|
-
* These downloads use an authenticated raw GET (the shared client's JSON path
|
|
9
|
-
* would corrupt binary PDFs), with the same auth headers the client sends.
|
|
10
|
-
*/
|
|
11
|
-
import { realpathSync, statSync, writeFileSync } from 'node:fs';
|
|
12
|
-
import { dirname, isAbsolute } from 'node:path';
|
|
13
|
-
import { z } from 'zod';
|
|
14
|
-
import { VERSION } from '../version.js';
|
|
15
|
-
const INLINE_CSV_LIMIT = 100 * 1024;
|
|
16
|
-
const readOnly = { readOnlyHint: true };
|
|
17
|
-
// Common optional filter fields shared across the accounting reads.
|
|
18
|
-
const labelId = z.number().int().positive().optional().describe('Filter by label id.');
|
|
19
|
-
const releaseId = z.number().int().positive().optional().describe('Filter by release id.');
|
|
20
|
-
const isrc = z.string().optional().describe('Filter by ISRC.');
|
|
21
|
-
const upc = z.string().optional().describe('Filter by UPC/barcode.');
|
|
22
|
-
const startDate = z.string().optional().describe('Range start, YYYY-MM-DD.');
|
|
23
|
-
const endDate = z.string().optional().describe('Range end, YYYY-MM-DD.');
|
|
24
|
-
const perPage = z.number().int().positive().optional().describe('Items per page.');
|
|
25
|
-
/**
|
|
26
|
-
* Validates that save_to_path is absolute and its parent resolves (via
|
|
27
|
-
* realpathSync, so a dangling/symlinked parent is rejected) to an existing real
|
|
28
|
-
* directory. Writing itself is exclusive (see writeNewFile), so this never
|
|
29
|
-
* overwrites an existing file.
|
|
30
|
-
*/
|
|
31
|
-
function validateSavePath(p) {
|
|
32
|
-
if (!isAbsolute(p)) {
|
|
33
|
-
return {
|
|
34
|
-
code: 'INVALID_PATH',
|
|
35
|
-
message: `save_to_path must be an absolute path (received: ${p}).`,
|
|
36
|
-
status: 0,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
const dir = dirname(p);
|
|
40
|
-
let realDir;
|
|
41
|
-
try {
|
|
42
|
-
realDir = realpathSync(dir);
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
return {
|
|
46
|
-
code: 'INVALID_PATH',
|
|
47
|
-
message: `The parent directory of save_to_path does not exist: ${dir}.`,
|
|
48
|
-
status: 0,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
let isDir = false;
|
|
52
|
-
try {
|
|
53
|
-
isDir = statSync(realDir).isDirectory();
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
isDir = false;
|
|
57
|
-
}
|
|
58
|
-
if (!isDir) {
|
|
59
|
-
return {
|
|
60
|
-
code: 'INVALID_PATH',
|
|
61
|
-
message: `The parent directory of save_to_path is not a directory: ${dir}.`,
|
|
62
|
-
status: 0,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Writes a file with exclusive creation ('wx'): an existing path is NEVER
|
|
69
|
-
* overwritten. Returns FILE_EXISTS on collision, or a structured write error,
|
|
70
|
-
* or null on success.
|
|
71
|
-
*/
|
|
72
|
-
function writeNewFile(path, data) {
|
|
73
|
-
try {
|
|
74
|
-
writeFileSync(path, data, { flag: 'wx' });
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
catch (err) {
|
|
78
|
-
if (err.code === 'EEXIST') {
|
|
79
|
-
return {
|
|
80
|
-
code: 'FILE_EXISTS',
|
|
81
|
-
message: `A file already exists at ${path}. This tool never overwrites — choose a new path.`,
|
|
82
|
-
status: 0,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
code: 'WRITE_FAILED',
|
|
87
|
-
message: `Could not write to ${path}: ${err instanceof Error ? err.message : 'unknown error'}.`,
|
|
88
|
-
status: 0,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/** Maps an error HTTP status from a raw download into a structured code. */
|
|
93
|
-
function statusToCode(status) {
|
|
94
|
-
if (status === 401)
|
|
95
|
-
return 'TOKEN_INVALID';
|
|
96
|
-
if (status === 403)
|
|
97
|
-
return 'FORBIDDEN';
|
|
98
|
-
if (status === 404)
|
|
99
|
-
return 'NOT_FOUND';
|
|
100
|
-
if (status >= 500)
|
|
101
|
-
return 'SERVER_ERROR';
|
|
102
|
-
return 'ERROR';
|
|
103
|
-
}
|
|
104
|
-
/** Authenticated raw GET for file downloads; returns the Response or an error. */
|
|
105
|
-
async function authedGet(ctx, path) {
|
|
106
|
-
const base = ctx.config.baseUrl.replace(/\/+$/, '');
|
|
107
|
-
let res;
|
|
108
|
-
try {
|
|
109
|
-
res = await ctx.client.raw(`${base}${path}`, {
|
|
110
|
-
method: 'GET',
|
|
111
|
-
headers: {
|
|
112
|
-
Authorization: `Bearer ${ctx.config.token}`,
|
|
113
|
-
Accept: 'application/json',
|
|
114
|
-
'User-Agent': `labelgrid-mcp/${VERSION}`,
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
catch (err) {
|
|
119
|
-
return {
|
|
120
|
-
ok: false,
|
|
121
|
-
error: {
|
|
122
|
-
code: 'NETWORK_ERROR',
|
|
123
|
-
message: err instanceof Error ? err.message : 'Network request failed.',
|
|
124
|
-
status: 0,
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
if (!res.ok) {
|
|
129
|
-
let message = `Request failed with status ${res.status}.`;
|
|
130
|
-
try {
|
|
131
|
-
const text = await res.text();
|
|
132
|
-
if (text) {
|
|
133
|
-
try {
|
|
134
|
-
const body = JSON.parse(text);
|
|
135
|
-
if (typeof body.message === 'string')
|
|
136
|
-
message = body.message;
|
|
137
|
-
else if (typeof body.error === 'string')
|
|
138
|
-
message = body.error;
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
message = text;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
// keep the default message
|
|
147
|
-
}
|
|
148
|
-
return { ok: false, error: { code: statusToCode(res.status), message, status: res.status } };
|
|
149
|
-
}
|
|
150
|
-
return { ok: true, res };
|
|
151
|
-
}
|
|
152
|
-
const listStatements = {
|
|
153
|
-
name: 'list_statements',
|
|
154
|
-
toolset: 'accounting',
|
|
155
|
-
gate: 'read',
|
|
156
|
-
title: 'List statements',
|
|
157
|
-
description: 'List your royalty statements, paginated. Filter by label_id, release_id, isrc, upc, and a start_date/end_date range. Pass group_by="release" to roll the totals up per release. Use get_statement for one statement, or download_statement_csv for its line items.',
|
|
158
|
-
inputShape: {
|
|
159
|
-
group_by: z.enum(['release']).optional().describe('Roll totals up per release.'),
|
|
160
|
-
label_id: labelId,
|
|
161
|
-
release_id: releaseId,
|
|
162
|
-
isrc,
|
|
163
|
-
upc,
|
|
164
|
-
start_date: startDate,
|
|
165
|
-
end_date: endDate,
|
|
166
|
-
},
|
|
167
|
-
annotations: readOnly,
|
|
168
|
-
handler: (args, { client }) => {
|
|
169
|
-
const { group_by, label_id, release_id, isrc: i, upc: u, start_date, end_date } = args;
|
|
170
|
-
return client.get('/statements', {
|
|
171
|
-
group_by,
|
|
172
|
-
filter: { label_id, release_id, isrc: i, upc: u, start_date, end_date },
|
|
173
|
-
});
|
|
174
|
-
},
|
|
175
|
-
};
|
|
176
|
-
const getStatement = {
|
|
177
|
-
name: 'get_statement',
|
|
178
|
-
toolset: 'accounting',
|
|
179
|
-
gate: 'read',
|
|
180
|
-
title: 'Get a statement',
|
|
181
|
-
description: 'Retrieve one royalty statement by its invoice number.',
|
|
182
|
-
inputShape: { invoice_number: z.string().describe('The statement invoice number.') },
|
|
183
|
-
annotations: readOnly,
|
|
184
|
-
handler: (args, { client }) => client.get(`/statements/${encodeURIComponent(String(args.invoice_number))}`),
|
|
185
|
-
};
|
|
186
|
-
const downloadStatementCsv = {
|
|
187
|
-
name: 'download_statement_csv',
|
|
188
|
-
toolset: 'accounting',
|
|
189
|
-
gate: 'read',
|
|
190
|
-
title: 'Download statement CSV',
|
|
191
|
-
description: 'Download statement line items as CSV. Pass invoice_number for a single statement, OR a start_date/end_date range to export across statements. If save_to_path (an absolute path whose parent directory exists) is given, the CSV is written there — an existing file is never overwritten (returns FILE_EXISTS) — and the tool returns the byte count. Otherwise the CSV is returned inline, truncated at 100KB (with truncated: true) — use save_to_path for large exports.',
|
|
192
|
-
inputShape: {
|
|
193
|
-
invoice_number: z.string().optional().describe('Single-statement invoice number.'),
|
|
194
|
-
start_date: z.string().optional().describe('Export range start, YYYY-MM-DD.'),
|
|
195
|
-
end_date: z.string().optional().describe('Export range end, YYYY-MM-DD.'),
|
|
196
|
-
save_to_path: z
|
|
197
|
-
.string()
|
|
198
|
-
.optional()
|
|
199
|
-
.describe('Absolute path (existing parent dir) to write the CSV to instead of returning it inline.'),
|
|
200
|
-
},
|
|
201
|
-
annotations: readOnly,
|
|
202
|
-
handler: async (args, ctx) => {
|
|
203
|
-
const savePath = args.save_to_path;
|
|
204
|
-
if (savePath !== undefined) {
|
|
205
|
-
const err = validateSavePath(savePath);
|
|
206
|
-
if (err)
|
|
207
|
-
return { error: err };
|
|
208
|
-
}
|
|
209
|
-
const invoice = args.invoice_number;
|
|
210
|
-
let path;
|
|
211
|
-
if (invoice !== undefined && invoice !== '') {
|
|
212
|
-
path = `/statements/${encodeURIComponent(invoice)}/csv`;
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
const parts = [];
|
|
216
|
-
if (args.start_date !== undefined)
|
|
217
|
-
parts.push(`start_date=${encodeURIComponent(String(args.start_date))}`);
|
|
218
|
-
if (args.end_date !== undefined)
|
|
219
|
-
parts.push(`end_date=${encodeURIComponent(String(args.end_date))}`);
|
|
220
|
-
path = `/statements/export/csv${parts.length > 0 ? `?${parts.join('&')}` : ''}`;
|
|
221
|
-
}
|
|
222
|
-
const result = await authedGet(ctx, path);
|
|
223
|
-
if (!result.ok)
|
|
224
|
-
return { error: result.error };
|
|
225
|
-
const text = await result.res.text();
|
|
226
|
-
const totalBytes = Buffer.byteLength(text);
|
|
227
|
-
if (savePath !== undefined) {
|
|
228
|
-
const writeErr = writeNewFile(savePath, text);
|
|
229
|
-
if (writeErr)
|
|
230
|
-
return { error: writeErr };
|
|
231
|
-
return { data: { saved_to: savePath, bytes: totalBytes } };
|
|
232
|
-
}
|
|
233
|
-
const truncated = text.length > INLINE_CSV_LIMIT;
|
|
234
|
-
const content = truncated ? text.slice(0, INLINE_CSV_LIMIT) : text;
|
|
235
|
-
return { data: { content, truncated, bytes: totalBytes } };
|
|
236
|
-
},
|
|
237
|
-
};
|
|
238
|
-
const downloadStatementInvoice = {
|
|
239
|
-
name: 'download_statement_invoice',
|
|
240
|
-
toolset: 'accounting',
|
|
241
|
-
gate: 'read',
|
|
242
|
-
title: 'Download statement invoice PDF',
|
|
243
|
-
description: 'Download the invoice PDF for a statement. save_to_path is REQUIRED (the PDF is binary) and must be an absolute path whose parent directory exists; the PDF is written there — an existing file is never overwritten (returns FILE_EXISTS) — and the tool returns the byte count.',
|
|
244
|
-
inputShape: {
|
|
245
|
-
invoice_number: z.string().describe('The statement invoice number.'),
|
|
246
|
-
save_to_path: z
|
|
247
|
-
.string()
|
|
248
|
-
.describe('Absolute path (existing parent dir) to write the PDF to. Required.'),
|
|
249
|
-
},
|
|
250
|
-
annotations: readOnly,
|
|
251
|
-
handler: async (args, ctx) => {
|
|
252
|
-
const savePath = args.save_to_path;
|
|
253
|
-
const err = validateSavePath(savePath);
|
|
254
|
-
if (err)
|
|
255
|
-
return { error: err };
|
|
256
|
-
const invoice = encodeURIComponent(String(args.invoice_number));
|
|
257
|
-
const result = await authedGet(ctx, `/statements/${invoice}/invoice`);
|
|
258
|
-
if (!result.ok)
|
|
259
|
-
return { error: result.error };
|
|
260
|
-
const bytes = Buffer.from(await result.res.arrayBuffer());
|
|
261
|
-
const writeErr = writeNewFile(savePath, bytes);
|
|
262
|
-
if (writeErr)
|
|
263
|
-
return { error: writeErr };
|
|
264
|
-
return { data: { saved_to: savePath, bytes: bytes.length } };
|
|
265
|
-
},
|
|
266
|
-
};
|
|
267
|
-
const listTransactions = {
|
|
268
|
-
name: 'list_transactions',
|
|
269
|
-
toolset: 'accounting',
|
|
270
|
-
gate: 'read',
|
|
271
|
-
title: 'List transactions',
|
|
272
|
-
description: 'List account transactions, paginated. Filter by label_id, release_id, isrc, upc, and a start_date/end_date range; sort with `sort`; pass group_by="release" to roll up per release.',
|
|
273
|
-
inputShape: {
|
|
274
|
-
group_by: z.enum(['release']).optional(),
|
|
275
|
-
page: z.number().int().positive().optional(),
|
|
276
|
-
per_page: perPage,
|
|
277
|
-
sort: z.string().optional().describe('Sort expression.'),
|
|
278
|
-
label_id: labelId,
|
|
279
|
-
release_id: releaseId,
|
|
280
|
-
isrc,
|
|
281
|
-
upc,
|
|
282
|
-
start_date: startDate,
|
|
283
|
-
end_date: endDate,
|
|
284
|
-
},
|
|
285
|
-
annotations: readOnly,
|
|
286
|
-
handler: (args, { client }) => {
|
|
287
|
-
const { group_by, page, per_page, sort, label_id, release_id, isrc: i, upc: u, start_date, end_date, } = args;
|
|
288
|
-
return client.get('/transactions', {
|
|
289
|
-
group_by,
|
|
290
|
-
page,
|
|
291
|
-
per_page,
|
|
292
|
-
sort,
|
|
293
|
-
filter: { label_id, release_id, isrc: i, upc: u, start_date, end_date },
|
|
294
|
-
});
|
|
295
|
-
},
|
|
296
|
-
};
|
|
297
|
-
const getRoyaltiesBreakdown = {
|
|
298
|
-
name: 'get_royalties_breakdown',
|
|
299
|
-
toolset: 'accounting',
|
|
300
|
-
gate: 'read',
|
|
301
|
-
title: 'Get royalties breakdown',
|
|
302
|
-
description: 'Get a cursor-paginated royalty breakdown grouped by one or more dimensions. group_by is REQUIRED and is a comma-separated, ordered subset of: track, dsp, release, territory, period (e.g. "release,dsp"). Filter by label_id, release_id, isrc, upc, and a start_date/end_date range.',
|
|
303
|
-
inputShape: {
|
|
304
|
-
group_by: z
|
|
305
|
-
.string()
|
|
306
|
-
.describe('Required. Comma-separated, ordered subset of: track, dsp, release, territory, period.'),
|
|
307
|
-
per_page: perPage,
|
|
308
|
-
cursor: z.string().optional().describe('Pagination cursor.'),
|
|
309
|
-
label_id: labelId,
|
|
310
|
-
release_id: releaseId,
|
|
311
|
-
isrc,
|
|
312
|
-
upc,
|
|
313
|
-
start_date: startDate,
|
|
314
|
-
end_date: endDate,
|
|
315
|
-
},
|
|
316
|
-
annotations: readOnly,
|
|
317
|
-
handler: (args, { client }) => {
|
|
318
|
-
const { group_by, per_page, cursor, label_id, release_id, isrc: i, upc: u, start_date, end_date, } = args;
|
|
319
|
-
return client.get('/royalties/breakdown', {
|
|
320
|
-
group_by,
|
|
321
|
-
per_page,
|
|
322
|
-
cursor,
|
|
323
|
-
filter: { label_id, release_id, isrc: i, upc: u, start_date, end_date },
|
|
324
|
-
});
|
|
325
|
-
},
|
|
326
|
-
};
|
|
327
|
-
const listArtificialStreams = {
|
|
328
|
-
name: 'list_artificial_streams',
|
|
329
|
-
toolset: 'accounting',
|
|
330
|
-
gate: 'read',
|
|
331
|
-
title: 'List artificial-streaming records',
|
|
332
|
-
description: 'List the artificial-streaming records reported for your catalog, cursor-paginated — the per-record detail behind any artificial-streaming fee. Filter by dsp (spotify or apple), a start_date/end_date range, release_id, or isrc.',
|
|
333
|
-
inputShape: {
|
|
334
|
-
dsp: z.enum(['spotify', 'apple']).optional().describe('Filter by platform.'),
|
|
335
|
-
start_date: startDate,
|
|
336
|
-
end_date: endDate,
|
|
337
|
-
release_id: releaseId,
|
|
338
|
-
isrc,
|
|
339
|
-
cursor: z.string().optional().describe('Pagination cursor.'),
|
|
340
|
-
per_page: perPage,
|
|
341
|
-
},
|
|
342
|
-
annotations: readOnly,
|
|
343
|
-
handler: (args, { client }) => {
|
|
344
|
-
const { dsp, start_date, end_date, release_id, isrc: i, cursor, per_page } = args;
|
|
345
|
-
return client.get('/royalties/artificial-streams', {
|
|
346
|
-
dsp,
|
|
347
|
-
start_date,
|
|
348
|
-
end_date,
|
|
349
|
-
release_id,
|
|
350
|
-
isrc: i,
|
|
351
|
-
cursor,
|
|
352
|
-
per_page,
|
|
353
|
-
});
|
|
354
|
-
},
|
|
355
|
-
};
|
|
356
|
-
const getArtificialFeeBreakdown = {
|
|
357
|
-
name: 'get_artificial_fee_breakdown',
|
|
358
|
-
toolset: 'accounting',
|
|
359
|
-
gate: 'read',
|
|
360
|
-
title: 'Get artificial-streaming fee breakdown',
|
|
361
|
-
description: 'Retrieve the per-release breakdown of an artificial-streaming fee for one billing period. `period` is the month in YYYY-MM format.',
|
|
362
|
-
inputShape: { period: z.string().describe('Billing month, YYYY-MM.') },
|
|
363
|
-
annotations: readOnly,
|
|
364
|
-
handler: (args, { client }) => client.get(`/artificial-streaming-fee/${encodeURIComponent(String(args.period))}`),
|
|
365
|
-
};
|
|
366
|
-
const getAccountSummary = {
|
|
367
|
-
name: 'get_account_summary',
|
|
368
|
-
toolset: 'accounting',
|
|
369
|
-
gate: 'read',
|
|
370
|
-
title: 'Get account summary',
|
|
371
|
-
description: 'Retrieve your accounting summary — current balance and related account-level financial totals.',
|
|
372
|
-
inputShape: {},
|
|
373
|
-
annotations: readOnly,
|
|
374
|
-
handler: (_args, { client }) => client.get('/account'),
|
|
375
|
-
};
|
|
376
|
-
export const accountingTools = [
|
|
377
|
-
listStatements,
|
|
378
|
-
getStatement,
|
|
379
|
-
downloadStatementCsv,
|
|
380
|
-
downloadStatementInvoice,
|
|
381
|
-
listTransactions,
|
|
382
|
-
getRoyaltiesBreakdown,
|
|
383
|
-
listArtificialStreams,
|
|
384
|
-
getArtificialFeeBreakdown,
|
|
385
|
-
getAccountSummary,
|
|
386
|
-
];
|
package/dist/tools/analytics.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/** Analytics toolset: one tool wrapping the streaming analytics summary. */
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
/** The 15 metric sections the summary endpoint can return. */
|
|
4
|
-
const METRICS = [
|
|
5
|
-
'streams',
|
|
6
|
-
'listeners',
|
|
7
|
-
'saves',
|
|
8
|
-
'skips',
|
|
9
|
-
'shares',
|
|
10
|
-
'completion-rate',
|
|
11
|
-
'lyrics-view-rate',
|
|
12
|
-
'canvas-view-rate',
|
|
13
|
-
'device-split',
|
|
14
|
-
'source-split',
|
|
15
|
-
'saves-by-tier',
|
|
16
|
-
'streams-by-country',
|
|
17
|
-
'streams-by-gender',
|
|
18
|
-
'streams-by-age',
|
|
19
|
-
'shares-by-country',
|
|
20
|
-
];
|
|
21
|
-
const getAnalytics = {
|
|
22
|
-
name: 'get_analytics',
|
|
23
|
-
toolset: 'analytics',
|
|
24
|
-
gate: 'read',
|
|
25
|
-
title: 'Get streaming analytics',
|
|
26
|
-
description: 'Retrieve a streaming analytics summary for your catalog in a single call. `start_date` and `end_date` (both YYYY-MM-DD) are required and the window is capped at 30 days by the server. ' +
|
|
27
|
-
'Optionally narrow the result by `platform` (SPOTIFY, ITUNES, APPLE_MUSIC), `release_id`, `isrc`, `upc`, or `artist_names`. ' +
|
|
28
|
-
'By default all 15 metric sections are returned; pass `metrics` to request only a subset. Available metrics: ' +
|
|
29
|
-
'streams, listeners, saves, skips, shares, completion-rate, lyrics-view-rate, canvas-view-rate, device-split, source-split, saves-by-tier, streams-by-country, streams-by-gender, streams-by-age, shares-by-country. ' +
|
|
30
|
-
'This endpoint is rate-limited (about 60 requests per minute); a 429 response carries retry_after_seconds.',
|
|
31
|
-
inputShape: {
|
|
32
|
-
start_date: z.string().describe('Start of the reporting window, YYYY-MM-DD. Required.'),
|
|
33
|
-
end_date: z
|
|
34
|
-
.string()
|
|
35
|
-
.describe('End of the reporting window, YYYY-MM-DD. Required. Max 30-day span.'),
|
|
36
|
-
metrics: z
|
|
37
|
-
.array(z.enum(METRICS))
|
|
38
|
-
.optional()
|
|
39
|
-
.describe('Subset of metric sections to return; omit for all 15.'),
|
|
40
|
-
platform: z.enum(['SPOTIFY', 'ITUNES', 'APPLE_MUSIC']).optional(),
|
|
41
|
-
release_id: z.number().int().positive().optional(),
|
|
42
|
-
isrc: z.string().optional(),
|
|
43
|
-
upc: z.string().optional(),
|
|
44
|
-
artist_names: z.array(z.string()).optional().describe('Filter to one or more artist names.'),
|
|
45
|
-
limit: z.number().int().positive().optional(),
|
|
46
|
-
},
|
|
47
|
-
annotations: { readOnlyHint: true },
|
|
48
|
-
handler: (args, { client }) => client.get('/analytics/summary', {
|
|
49
|
-
filter: {
|
|
50
|
-
start_date: args.start_date,
|
|
51
|
-
end_date: args.end_date,
|
|
52
|
-
platform: args.platform,
|
|
53
|
-
release_id: args.release_id,
|
|
54
|
-
isrc: args.isrc,
|
|
55
|
-
upc: args.upc,
|
|
56
|
-
artist_names: args.artist_names,
|
|
57
|
-
},
|
|
58
|
-
metrics: args.metrics,
|
|
59
|
-
limit: args.limit,
|
|
60
|
-
}),
|
|
61
|
-
};
|
|
62
|
-
export const analyticsTools = [getAnalytics];
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Catalog read toolset: paginated collection listings and single-item reads for
|
|
3
|
-
* labels, artists, writers, publishers, releases and tracks.
|
|
4
|
-
*
|
|
5
|
-
* Every list tool accepts `page`/`per_page` plus the endpoint's documented
|
|
6
|
-
* `filter[...]` parameters; the handler nests the filter fields so the client
|
|
7
|
-
* serializes them as `filter[key]=value`.
|
|
8
|
-
*/
|
|
9
|
-
import type { ToolDef } from './types.js';
|
|
10
|
-
export declare const catalogReadTools: ToolDef[];
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Catalog read toolset: paginated collection listings and single-item reads for
|
|
3
|
-
* labels, artists, writers, publishers, releases and tracks.
|
|
4
|
-
*
|
|
5
|
-
* Every list tool accepts `page`/`per_page` plus the endpoint's documented
|
|
6
|
-
* `filter[...]` parameters; the handler nests the filter fields so the client
|
|
7
|
-
* serializes them as `filter[key]=value`.
|
|
8
|
-
*/
|
|
9
|
-
import { z } from 'zod';
|
|
10
|
-
const page = z.number().int().positive().optional().describe('1-based page number.');
|
|
11
|
-
const perPage = z.number().int().positive().optional().describe('Items per page.');
|
|
12
|
-
/** Builds a paginated list tool whose extra input fields become `filter[...]`. */
|
|
13
|
-
function listTool(spec) {
|
|
14
|
-
return {
|
|
15
|
-
name: spec.name,
|
|
16
|
-
toolset: 'catalog',
|
|
17
|
-
gate: 'read',
|
|
18
|
-
title: spec.title,
|
|
19
|
-
description: spec.description,
|
|
20
|
-
inputShape: { page, per_page: perPage, ...spec.filters },
|
|
21
|
-
annotations: { readOnlyHint: true },
|
|
22
|
-
handler: (args, { client }) => {
|
|
23
|
-
const { page: p, per_page: pp, ...filter } = args;
|
|
24
|
-
return client.get(spec.path, { page: p, per_page: pp, filter });
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
/** Builds a single-item read tool keyed by a positive integer id. */
|
|
29
|
-
function getTool(spec) {
|
|
30
|
-
return {
|
|
31
|
-
name: spec.name,
|
|
32
|
-
toolset: 'catalog',
|
|
33
|
-
gate: 'read',
|
|
34
|
-
title: spec.title,
|
|
35
|
-
description: spec.description,
|
|
36
|
-
inputShape: { [spec.idField]: z.number().int().positive() },
|
|
37
|
-
annotations: { readOnlyHint: true },
|
|
38
|
-
handler: (args, { client }) => client.get(`${spec.basePath}/${args[spec.idField]}`),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
export const catalogReadTools = [
|
|
42
|
-
listTool({
|
|
43
|
-
name: 'list_labels',
|
|
44
|
-
path: '/labels',
|
|
45
|
-
title: 'List labels',
|
|
46
|
-
description: 'List the labels in your account, paginated. A label groups your releases and carries default copyright, website and outlet settings. Use get_label for the full detail of one label.',
|
|
47
|
-
filters: {},
|
|
48
|
-
}),
|
|
49
|
-
getTool({
|
|
50
|
-
name: 'get_label',
|
|
51
|
-
idField: 'label_id',
|
|
52
|
-
basePath: '/labels',
|
|
53
|
-
title: 'Get a label',
|
|
54
|
-
description: 'Retrieve one label by id, including its settings and defaults.',
|
|
55
|
-
}),
|
|
56
|
-
listTool({
|
|
57
|
-
name: 'list_artists',
|
|
58
|
-
path: '/artists',
|
|
59
|
-
title: 'List artists',
|
|
60
|
-
description: 'List the artists in your account, paginated. Filter by `artist_name` to find a specific artist. Use get_artist for one artist’s full profile and links.',
|
|
61
|
-
filters: { artist_name: z.string().optional().describe('Filter by artist name.') },
|
|
62
|
-
}),
|
|
63
|
-
getTool({
|
|
64
|
-
name: 'get_artist',
|
|
65
|
-
idField: 'artist_id',
|
|
66
|
-
basePath: '/artists',
|
|
67
|
-
title: 'Get an artist',
|
|
68
|
-
description: 'Retrieve one artist by id, including bio, identifiers and platform links.',
|
|
69
|
-
}),
|
|
70
|
-
listTool({
|
|
71
|
-
name: 'list_writers',
|
|
72
|
-
path: '/writers',
|
|
73
|
-
title: 'List writers',
|
|
74
|
-
description: 'List the songwriters in your account, paginated. Filter by `name` or `ipi`. Writers are attached to tracks for composition credits and royalty splits.',
|
|
75
|
-
filters: {
|
|
76
|
-
name: z.string().optional().describe('Filter by writer name.'),
|
|
77
|
-
ipi: z.string().optional().describe('Filter by IPI number.'),
|
|
78
|
-
},
|
|
79
|
-
}),
|
|
80
|
-
getTool({
|
|
81
|
-
name: 'get_writer',
|
|
82
|
-
idField: 'writer_id',
|
|
83
|
-
basePath: '/writers',
|
|
84
|
-
title: 'Get a writer',
|
|
85
|
-
description: 'Retrieve one writer by id, including PRO/IPI identifiers and publisher link.',
|
|
86
|
-
}),
|
|
87
|
-
listTool({
|
|
88
|
-
name: 'list_publishers',
|
|
89
|
-
path: '/publishers',
|
|
90
|
-
title: 'List publishers',
|
|
91
|
-
description: 'List the publishers in your account, paginated. Filter by `name` or `ipi`. Publishers are linked to writers for publishing administration.',
|
|
92
|
-
filters: {
|
|
93
|
-
name: z.string().optional().describe('Filter by publisher name.'),
|
|
94
|
-
ipi: z.string().optional().describe('Filter by IPI number.'),
|
|
95
|
-
},
|
|
96
|
-
}),
|
|
97
|
-
getTool({
|
|
98
|
-
name: 'get_publisher',
|
|
99
|
-
idField: 'publisher_id',
|
|
100
|
-
basePath: '/publishers',
|
|
101
|
-
title: 'Get a publisher',
|
|
102
|
-
description: 'Retrieve one publisher by id.',
|
|
103
|
-
}),
|
|
104
|
-
listTool({
|
|
105
|
-
name: 'list_releases',
|
|
106
|
-
path: '/releases',
|
|
107
|
-
title: 'List releases',
|
|
108
|
-
description: 'List releases in your account, paginated. Filter by `label_id`, `is_live` (1 = live/distributed), `barcode_number` (UPC/EAN), or `cat` (catalog number). Use get_release for one release’s full metadata and track listing.',
|
|
109
|
-
filters: {
|
|
110
|
-
label_id: z.number().int().positive().optional().describe('Filter by owning label id.'),
|
|
111
|
-
is_live: z.number().int().optional().describe('1 to return only live/distributed releases.'),
|
|
112
|
-
barcode_number: z.string().optional().describe('Filter by UPC/EAN barcode.'),
|
|
113
|
-
cat: z.string().optional().describe('Filter by catalog number.'),
|
|
114
|
-
},
|
|
115
|
-
}),
|
|
116
|
-
getTool({
|
|
117
|
-
name: 'get_release',
|
|
118
|
-
idField: 'release_id',
|
|
119
|
-
basePath: '/releases',
|
|
120
|
-
title: 'Get a release',
|
|
121
|
-
description: 'Retrieve one release by id, including its metadata, artwork state and track listing.',
|
|
122
|
-
}),
|
|
123
|
-
listTool({
|
|
124
|
-
name: 'list_tracks',
|
|
125
|
-
path: '/tracks',
|
|
126
|
-
title: 'List tracks',
|
|
127
|
-
description: 'List tracks in your account, paginated. Filter by `release_id` to list a release’s tracks, or by `isrc`. Use get_track for one track’s full metadata, credits and splits.',
|
|
128
|
-
filters: {
|
|
129
|
-
release_id: z
|
|
130
|
-
.number()
|
|
131
|
-
.int()
|
|
132
|
-
.positive()
|
|
133
|
-
.optional()
|
|
134
|
-
.describe('Filter to one release’s tracks.'),
|
|
135
|
-
isrc: z.string().optional().describe('Filter by ISRC.'),
|
|
136
|
-
},
|
|
137
|
-
}),
|
|
138
|
-
getTool({
|
|
139
|
-
name: 'get_track',
|
|
140
|
-
idField: 'track_id',
|
|
141
|
-
basePath: '/tracks',
|
|
142
|
-
title: 'Get a track',
|
|
143
|
-
description: 'Retrieve one track by id, including titles, contributors, writers, publishers and royalty splits.',
|
|
144
|
-
}),
|
|
145
|
-
];
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Catalog write toolset (safe writes): create/update/delete for labels,
|
|
3
|
-
* artists, writers and publishers, plus multipart image uploads.
|
|
4
|
-
*
|
|
5
|
-
* Create/update forward a permissive `fields` object straight to the API, which
|
|
6
|
-
* owns all validation — this keeps each tool a thin wrapper and lets the full
|
|
7
|
-
* documented field set through without re-declaring it here. The tool
|
|
8
|
-
* descriptions name the required and common fields; consult the LabelGrid API
|
|
9
|
-
* docs for the complete list.
|
|
10
|
-
*/
|
|
11
|
-
import type { ToolDef } from './types.js';
|
|
12
|
-
export declare const catalogWriteTools: ToolDef[];
|