@labelgrid/mcp
Advanced tools
+36
-0
@@ -8,2 +8,38 @@ # Changelog | ||
| ## [0.6.0] - 2026-08-05 | ||
| ### Added | ||
| - `get_analytics_rankings` — top-N rankings for a window, ordered by summed | ||
| streams. `view: 'leaderboards'` ranks top artists, tracks or albums (`type` | ||
| required); `view: 'placements'` ranks the playlists and radio containers | ||
| driving streams. | ||
| - `get_rate_limit` — the account's API rate budget per category, with the | ||
| ceiling, the remaining allowance and the window reset. | ||
| - 10 new analytics section keys (47 total): eight social and UGC sections | ||
| (`social-usage-over-time`, `social-reach-over-time`, `social-platform-mix`, | ||
| `social-top-tracks`, `social-territory`, `social-artist-reach`, | ||
| `social-artist-reach-daily`, `soundcloud-engagement`) and two per-track daily | ||
| series (`track-streams-daily`, `track-listeners-daily`, which need a | ||
| `release_id`, `isrc` or `upc` scope). | ||
| - `ugc_platform` filter on `get_analytics`, narrowing the social and UGC | ||
| sections. | ||
| - `AMAZON` as a `platform` filter value on `get_analytics` and | ||
| `get_analytics_rankings` (11 total), matching the platforms the API accepts. | ||
| - `query_financials` concise mode now returns the fee fields — `labelgrid_fee`, | ||
| `platform_fee_usd`, `ugc_fee_usd`, `labelgrid_rate` and `labelgrid_ugc_rate`. | ||
| Previously a fee was visible only with `response_format: 'detailed'`. | ||
| ### Changed | ||
| - Social and UGC sections report their own per-section availability in | ||
| `meta.social_availability`; the streaming availability matrix does not cover | ||
| them. A use, a view and a play are distinct quantities and are never summed | ||
| with each other or with streams. | ||
| - `track-listeners-daily` sums each platform track entry's daily count. Where a | ||
| track exists as more than one entry it is not a distinct count of people, and | ||
| it is not summable across dates. | ||
| - Tool descriptions across the catalog were tightened. No tool names, | ||
| parameters or behavior changed beyond the items above. | ||
| ## [0.5.0] - 2026-07-27 | ||
@@ -10,0 +46,0 @@ |
+3
-0
@@ -20,2 +20,3 @@ /** | ||
| 'GET /me': 'get_account', | ||
| 'GET /rate-limit': 'get_rate_limit', | ||
| 'DELETE /tokens/current': 'revoke_api_token', | ||
@@ -34,2 +35,4 @@ 'DELETE /tokens/{tokenId}': 'revoke_api_token', | ||
| 'GET /analytics/availability': 'get_analytics_availability', | ||
| 'GET /analytics/leaderboards': 'get_analytics_rankings', | ||
| 'GET /analytics/placements': 'get_analytics_rankings', | ||
| // catalog reads | ||
@@ -36,0 +39,0 @@ 'GET /labels': 'search_catalog', |
@@ -75,2 +75,7 @@ /** | ||
| 'gross_usd', | ||
| 'labelgrid_fee', | ||
| 'platform_fee_usd', | ||
| 'ugc_fee_usd', | ||
| 'labelgrid_rate', | ||
| 'labelgrid_ugc_rate', | ||
| 'net_usd', | ||
@@ -77,0 +82,0 @@ 'amount', |
@@ -1,3 +0,6 @@ | ||
| /** Account toolset: read the authenticated account, revoke API tokens. */ | ||
| /** | ||
| * Account toolset: read the authenticated account, read its rate budget, revoke | ||
| * API tokens. | ||
| */ | ||
| import type { ToolDef } from './types.js'; | ||
| export declare const accountTools: ToolDef[]; |
@@ -1,2 +0,5 @@ | ||
| /** Account toolset: read the authenticated account, revoke API tokens. */ | ||
| /** | ||
| * Account toolset: read the authenticated account, read its rate budget, revoke | ||
| * API tokens. | ||
| */ | ||
| import { z } from 'zod'; | ||
@@ -17,2 +20,13 @@ const getAccount = { | ||
| }; | ||
| const getRateLimit = { | ||
| name: 'get_rate_limit', | ||
| toolset: 'account', | ||
| gate: 'read', | ||
| title: 'Get the rate budget', | ||
| description: 'The account’s API rate budget per category (read, write, export, analytics): ceiling, remaining, and window reset. ' + | ||
| 'Every token on the account shares one budget; a null ceiling means none applies. Free to call — use it to pace requests and to recover from a 429.', | ||
| inputShape: {}, | ||
| annotations: { readOnlyHint: true }, | ||
| handler: (_args, { client }) => client.get('/rate-limit'), | ||
| }; | ||
| const revokeApiToken = { | ||
@@ -33,2 +47,2 @@ name: 'revoke_api_token', | ||
| }; | ||
| export const accountTools = [getAccount, revokeApiToken]; | ||
| export const accountTools = [getAccount, getRateLimit, revokeApiToken]; |
@@ -24,3 +24,3 @@ /** | ||
| .optional() | ||
| .describe("'concise' (default) keeps high-signal fields (ids always kept); 'detailed' returns the verbatim API response."); | ||
| .describe("'concise' (default) keeps high-signal fields and ids; 'detailed' is the verbatim response."); | ||
| /** A permissive body of API fields, forwarded verbatim to the endpoint. */ | ||
@@ -208,3 +208,3 @@ function fieldsBody(desc) { | ||
| "(3) mode='download_url' + parent='track' + asset audio_16|audio_24|audio_32 (WAV master) or audio_preview_full|audio_preview_clip (MP3 preview) — returns { download_url, expires_in }, a signed URL that expires roughly 10 minutes after issue; fetch it directly — do not send your API token to it. " + | ||
| 'Any other combination returns a structured error.', | ||
| 'Any other combination is refused.', | ||
| inputShape: { | ||
@@ -226,3 +226,3 @@ parent: z.enum(['track', 'release']).describe('Whose asset.'), | ||
| ]) | ||
| .describe('Which asset — see the description for the valid parent/mode pairings.'), | ||
| .describe('Which asset — see the valid combinations above.'), | ||
| mode: z | ||
@@ -229,0 +229,0 @@ .enum(['info', 'download_url']) |
@@ -49,4 +49,4 @@ /** | ||
| .describe('Where the license came from.'), | ||
| license_provider_name: z.string().optional().describe('The name of the license provider.'), | ||
| original_track_link: z.string().optional().describe('URL to the original/source track.'), | ||
| license_provider_name: z.string().optional(), | ||
| original_track_link: z.string().optional().describe('URL to the source track.'), | ||
| }; | ||
@@ -74,6 +74,6 @@ /** Collects the defined license metadata fields into a string map for multipart. */ | ||
| title: 'Upload a release/track asset', | ||
| description: 'Upload a finalized track or release asset from a local file. `id` is the track id for track_* targets, the release id for release_*. ' + | ||
| description: 'Upload a finalized track or release asset from a local file. ' + | ||
| '`track_stereo` (WAV/FLAC/AIFF), `track_dolby` (Dolby Atmos WAV) and `track_lyrics` (LRC) process asynchronously — check state with get_asset (mode info). ' + | ||
| '`release_cover_art` uploads or replaces the static cover art image. ' + | ||
| '`release_motion_square` / `release_motion_tall` upload the square or tall animated cover (motion artwork) video, also asynchronous. ' + | ||
| '`release_motion_square`/`release_motion_tall` upload the animated cover (motion artwork) video, also asynchronous. ' + | ||
| 'All assets become immutable once the release is distributed — upload final files first.', | ||
@@ -91,3 +91,3 @@ inputShape: { | ||
| .describe('Which asset to upload.'), | ||
| id: z.number().int().positive().describe('The track id (track_*) or release id (release_*).'), | ||
| id: z.number().int().positive().describe('The track or release id, per `target`.'), | ||
| file_path: z.string().describe('Local path to the file to upload.'), | ||
@@ -176,3 +176,3 @@ }, | ||
| .optional() | ||
| .describe('From list_track_licenses. Required for update/delete.'), | ||
| .describe('Required for update/delete.'), | ||
| file_path: z | ||
@@ -179,0 +179,0 @@ .string() |
@@ -298,3 +298,3 @@ /** | ||
| '`statement_detail` retrieves one statement by `invoice_number` (required). ' + | ||
| '`transactions` lists account transactions, paginated — same `filters`; sort with `sort`; group_by="release" rolls up per release. ' + | ||
| '`transactions` lists account transactions, paginated — same `filters` and `group_by`; sort with `sort`. ' + | ||
| '`royalty_breakdown` returns a cursor-paginated royalty breakdown — `group_by` is REQUIRED for this view: a comma-separated, ordered subset of: track, dsp, release, territory, period (e.g. "release,dsp"); same `filters`; pass `cursor` to page. ' + | ||
@@ -310,3 +310,3 @@ "Use download_statement for statement line items (CSV) or the invoice PDF. response_format:'detailed' returns the verbatim API response.", | ||
| .optional() | ||
| .describe('REQUIRED for royalty_breakdown (ordered subset: track, dsp, release, territory, period); "release" rolls statements/transactions up per release.'), | ||
| .describe('Required for royalty_breakdown; "release" rolls statements/transactions up per release.'), | ||
| sort: z.string().optional().describe('Sort expression (view transactions).'), | ||
@@ -316,3 +316,3 @@ filters: z | ||
| .optional() | ||
| .describe('label_id, release_id, isrc, upc, start_date, end_date — passed through verbatim.'), | ||
| .describe('Filter names → values, passed through verbatim.'), | ||
| cursor: z.string().optional().describe('Pagination cursor (view royalty_breakdown).'), | ||
@@ -324,3 +324,3 @@ page: z.number().int().positive().optional().describe('1-based page number.'), | ||
| .optional() | ||
| .describe("'concise' (default) keeps only the high-signal fields (ids always kept); 'detailed' returns the verbatim API response."), | ||
| .describe("'concise' (default) or 'detailed'."), | ||
| }, | ||
@@ -376,7 +376,5 @@ annotations: { readOnlyHint: true }, | ||
| title: 'Download a statement file', | ||
| description: "Download statement files. `format: 'csv'` downloads statement line items — pass invoice_number for one statement, OR a start_date/end_date range to export across statements; with save_to_path (an absolute path whose parent directory exists) the CSV is written there and the byte count returned; otherwise it is returned inline, truncated at 100KB (truncated: true) — use save_to_path for large exports. `format: 'invoice_pdf'` downloads the invoice PDF — invoice_number and save_to_path are both REQUIRED (the PDF is binary). An existing file is never overwritten (returns FILE_EXISTS).", | ||
| description: "Download statement files. `format: 'csv'` downloads statement line items — pass invoice_number for one statement, OR a start_date/end_date range to export across statements; with save_to_path the CSV is written there and the byte count returned; otherwise it is returned inline, truncated at 100KB (truncated: true) — use save_to_path for large exports. `format: 'invoice_pdf'` downloads the invoice PDF — invoice_number and save_to_path are both REQUIRED (the PDF is binary). An existing file is never overwritten (returns FILE_EXISTS).", | ||
| inputShape: { | ||
| format: z | ||
| .enum(['csv', 'invoice_pdf']) | ||
| .describe('Which file: csv (line items) or invoice_pdf (the invoice PDF).'), | ||
| format: z.enum(['csv', 'invoice_pdf']).describe('Which file.'), | ||
| invoice_number: z | ||
@@ -391,3 +389,3 @@ .string() | ||
| .optional() | ||
| .describe('Absolute path (existing parent dir) to write the file to. Optional for csv (otherwise returned inline); required for invoice_pdf.'), | ||
| .describe('Absolute path (its parent directory must exist) to write the file to.'), | ||
| }, | ||
@@ -394,0 +392,0 @@ annotations: { readOnlyHint: true }, |
| /** | ||
| * Insights toolset: the streaming analytics summary, the availability-discovery | ||
| * endpoint, and the consolidated artificial-streaming query (early-warning | ||
| * flags, reported records, and the fee breakdown). All read-only. | ||
| * endpoint, the top-N rankings (leaderboards and placements), and the | ||
| * consolidated artificial-streaming query (early-warning flags, reported | ||
| * records, and the fee breakdown). All read-only. | ||
| */ | ||
| import type { ToolDef } from './types.js'; | ||
| export declare const insightsTools: ToolDef[]; |
+109
-10
| /** | ||
| * Insights toolset: the streaming analytics summary, the availability-discovery | ||
| * endpoint, and the consolidated artificial-streaming query (early-warning | ||
| * flags, reported records, and the fee breakdown). All read-only. | ||
| * endpoint, the top-N rankings (leaderboards and placements), and the | ||
| * consolidated artificial-streaming query (early-warning flags, reported | ||
| * records, and the fee breakdown). All read-only. | ||
| */ | ||
@@ -9,4 +10,5 @@ import { z } from 'zod'; | ||
| /** | ||
| * The 37 metric sections the summary endpoint can return, in the server's | ||
| * canonical order. | ||
| * The 47 metric sections the summary endpoint can return, in the server's | ||
| * canonical order: the streaming sections first, then the social and UGC | ||
| * family, then the per-track daily series. | ||
| */ | ||
@@ -51,3 +53,28 @@ const METRICS = [ | ||
| 'shazams-by-state', | ||
| 'social-usage-over-time', | ||
| 'social-reach-over-time', | ||
| 'social-platform-mix', | ||
| 'social-top-tracks', | ||
| 'social-territory', | ||
| 'social-artist-reach', | ||
| 'social-artist-reach-daily', | ||
| 'soundcloud-engagement', | ||
| 'track-streams-daily', | ||
| 'track-listeners-daily', | ||
| ]; | ||
| /** | ||
| * The UGC platform values `filter[ugc_platform]` accepts. A separate axis from | ||
| * `platform`: it narrows the social and UGC sections only, and its values never | ||
| * appear in a streaming total or platform share. | ||
| */ | ||
| const UGC_PLATFORMS = [ | ||
| 'snapchat', | ||
| 'instagram', | ||
| 'facebook', | ||
| 'soundcloud', | ||
| 'tiktok', | ||
| 'whatsapp', | ||
| 'threads', | ||
| 'messenger', | ||
| ]; | ||
| /** The maximum number of section keys the server accepts per summary request. */ | ||
@@ -64,2 +91,3 @@ const MAX_METRICS_PER_REQUEST = 12; | ||
| 'AUDIOMACK', | ||
| 'AMAZON', | ||
| 'KUGOU', | ||
@@ -73,10 +101,12 @@ 'KUWO', | ||
| gate: 'read', | ||
| title: 'Get streaming analytics', | ||
| title: 'Get streaming and social analytics', | ||
| description: 'Streaming analytics summary. Window capped at 400 days; `metrics` takes 1-12 section keys per request (split larger selections — responses are cached). ' + | ||
| 'KUGOU/KUWO/QQMUSIC report weekly: one point per week carrying the whole week — never average it per day. `meta` carries `platform_cadence`, `section_granularity`, `sections_as_of` and `sections_complete_through` (later dates still filling in). ' + | ||
| 'Call get_analytics_availability first for section-per-platform support. ' + | ||
| 'The `social-*` / `soundcloud-engagement` sections cover social and UGC usage instead of streaming: their `platform` is a UGC platform; a use, view and play are distinct quantities, never summed with each other or with streams; `ugc_platform` narrows them. Selecting any adds `meta.social_availability` (which UGC platforms report each signal) — the streaming matrix excludes them. ' + | ||
| 'The `track-*-daily` sections need a `release_id`, `isrc` or `upc` scope. `track-listeners-daily` sums per-entry daily counts: not distinct people, not summable across dates. ' + | ||
| 'Rate-limited ~60/min; windows over 90 days draw a separate lower ~30/min budget — prefer shorter windows for polling. A 429 carries retry_after_seconds.', | ||
| inputShape: { | ||
| start_date: z.string().describe('Start of the reporting window, YYYY-MM-DD.'), | ||
| end_date: z.string().describe('End of the reporting window, YYYY-MM-DD (max 400-day span).'), | ||
| start_date: z.string().describe('Window start, YYYY-MM-DD.'), | ||
| end_date: z.string().describe('Window end, YYYY-MM-DD.'), | ||
| metrics: z | ||
@@ -86,8 +116,12 @@ .array(z.enum(METRICS)) | ||
| .max(MAX_METRICS_PER_REQUEST) | ||
| .describe('Section keys to return, 1-12 per request.'), | ||
| .describe('Section keys, 1-12 per request.'), | ||
| platform: z.enum(PLATFORMS).optional(), | ||
| ugc_platform: z | ||
| .enum(UGC_PLATFORMS) | ||
| .optional() | ||
| .describe('Narrows the social/UGC sections only.'), | ||
| release_id: z.number().int().positive().optional(), | ||
| isrc: z.string().optional(), | ||
| upc: z.string().optional(), | ||
| artist_names: z.array(z.string()).optional().describe('Filter to one or more artist names.'), | ||
| artist_names: z.array(z.string()).optional(), | ||
| limit: z.number().int().positive().optional(), | ||
@@ -101,2 +135,3 @@ }, | ||
| platform: args.platform, | ||
| ugc_platform: args.ugc_platform, | ||
| release_id: args.release_id, | ||
@@ -122,2 +157,65 @@ isrc: args.isrc, | ||
| }; | ||
| /** The two ranking reads, and the entity kinds a leaderboard can rank. */ | ||
| const RANKING_VIEWS = ['leaderboards', 'placements']; | ||
| const LEADERBOARD_TYPES = ['artists', 'tracks', 'albums', 'all']; | ||
| /** Upper bound the ranking endpoints place on `limit`. */ | ||
| const MAX_RANKING_LIMIT = 50; | ||
| const getAnalyticsRankings = { | ||
| name: 'get_analytics_rankings', | ||
| toolset: 'insights', | ||
| gate: 'read', | ||
| title: 'Get analytics rankings', | ||
| description: 'Top-N rankings for a window, ordered by summed streams. Pick ONE `view`: ' + | ||
| '`leaderboards` — your top artists, tracks or albums (`type` required; `all` returns all three in one request). ' + | ||
| '`placements` — the playlists and radio containers driving streams, summed across storefronts. ' + | ||
| 'Same scope filters as get_analytics; `limit` 1-50 (default 10). Under a `platform` filter, an `availability` of `not_available_for_platform` means that platform reports no ranking and `data` is empty.', | ||
| inputShape: { | ||
| view: z.enum(RANKING_VIEWS).describe('Which ranking read.'), | ||
| start_date: z.string().describe('Window start, YYYY-MM-DD.'), | ||
| end_date: z.string().describe('Window end, YYYY-MM-DD.'), | ||
| type: z.enum(LEADERBOARD_TYPES).optional().describe('Required for view leaderboards.'), | ||
| platform: z.enum(PLATFORMS).optional(), | ||
| ugc_platform: z.enum(UGC_PLATFORMS).optional(), | ||
| release_id: z.number().int().positive().optional(), | ||
| isrc: z.string().optional(), | ||
| upc: z.string().optional(), | ||
| artist_names: z.array(z.string()).optional(), | ||
| label_id: z | ||
| .number() | ||
| .int() | ||
| .positive() | ||
| .optional() | ||
| .describe('Narrow to one of your own labels; it can never widen scope.'), | ||
| limit: z.number().int().positive().max(MAX_RANKING_LIMIT).optional(), | ||
| }, | ||
| annotations: { readOnlyHint: true }, | ||
| handler: (args, { client }) => { | ||
| const leaderboards = args.view === 'leaderboards'; | ||
| if (leaderboards && args.type === undefined) { | ||
| return Promise.resolve({ | ||
| error: { | ||
| code: 'INVALID_SELECTOR', | ||
| message: "view 'leaderboards' requires `type` — artists, tracks, albums, or all. `type` does not apply to view 'placements'.", | ||
| status: 0, | ||
| }, | ||
| }); | ||
| } | ||
| return client.get(leaderboards ? '/analytics/leaderboards' : '/analytics/placements', { | ||
| filter: { | ||
| start_date: args.start_date, | ||
| end_date: args.end_date, | ||
| platform: args.platform, | ||
| ugc_platform: args.ugc_platform, | ||
| release_id: args.release_id, | ||
| isrc: args.isrc, | ||
| upc: args.upc, | ||
| artist_names: args.artist_names, | ||
| label_id: args.label_id, | ||
| }, | ||
| // `type` is a leaderboards-only parameter — never sent to placements. | ||
| type: leaderboards ? args.type : undefined, | ||
| limit: args.limit, | ||
| }); | ||
| }, | ||
| }; | ||
| const queryArtificialStreaming = { | ||
@@ -150,3 +248,3 @@ name: 'query_artificial_streaming', | ||
| .optional() | ||
| .describe("'concise' (default) keeps only the high-signal fields (ids always kept); 'detailed' returns the verbatim API response."), | ||
| .describe("'concise' (default) or 'detailed'."), | ||
| }, | ||
@@ -202,3 +300,4 @@ annotations: { readOnlyHint: true }, | ||
| getAnalyticsAvailability, | ||
| getAnalyticsRankings, | ||
| queryArtificialStreaming, | ||
| ]; |
@@ -10,7 +10,6 @@ /** Reference toolset: one tool serving all read-only lookup datasets. */ | ||
| description: 'Fetch a LabelGrid reference dataset used to resolve the IDs and codes the catalog and release tools expect. Pick ONE dataset with `type`: ' + | ||
| '`genres` and `genre_categories` (genre IDs), `languages` (audio/metadata language codes), `contributor_roles`, `instruments`, `distro_outlets` (the outlets/stores available to your account), ' + | ||
| '`territories` (country codes), `issue_definitions` (each review issue code’s title, description, severity and whether it blocks distribution; codes are string slugs), ' + | ||
| 'or `webhook_event_types` (every webhook event type with its payload schema). ' + | ||
| 'Call this when you need a valid ID or code. ' + | ||
| 'The same datasets are exposed as MCP resources at labelgrid://reference/{type}; this tool is the fallback for clients that don’t surface resources.', | ||
| '`genres` and `genre_categories` (genre IDs), `languages` (audio/metadata language codes), `contributor_roles`, `instruments`, `distro_outlets` (the outlets/stores available to you), ' + | ||
| '`territories` (country codes), `issue_definitions` (review issue codes — string slugs — with severity and whether they block distribution), ' + | ||
| 'or `webhook_event_types` (event types with their payload schemas). ' + | ||
| 'Also exposed as MCP resources at labelgrid://reference/{type}; this tool is the fallback.', | ||
| inputShape: { | ||
@@ -17,0 +16,0 @@ type: z.enum(REFERENCE_TYPES), |
@@ -16,3 +16,3 @@ /** | ||
| .optional() | ||
| .describe("'concise' (default) keeps only the high-signal fields (ids always kept); 'detailed' returns the verbatim API response."); | ||
| .describe("'concise' (default) keeps high-signal fields and ids; 'detailed' is the verbatim response."); | ||
| const getReleaseReview = { | ||
@@ -19,0 +19,0 @@ name: 'get_release_review', |
@@ -13,10 +13,7 @@ /** | ||
| title: 'List webhooks', | ||
| description: "Read your webhook subscriptions. `view: 'config'` (the default) lists the webhook subscriptions configured on your account — each with its URL, subscribed events and active state — or retrieves one subscription when `webhook_id` is given. " + | ||
| description: "Read your webhook subscriptions. `view: 'config'` (the default) lists them — URL, subscribed events, active state — or retrieves one when `webhook_id` is given. " + | ||
| "`view: 'logs'` retrieves the recent delivery log for a webhook (`webhook_id` required) — attempts, response codes and outcomes — to debug why events did or did not reach your endpoint.", | ||
| inputShape: { | ||
| webhook_id: webhookId, | ||
| view: z | ||
| .enum(['config', 'logs']) | ||
| .optional() | ||
| .describe('config (default) reads subscriptions; logs reads a webhook’s delivery log.'), | ||
| view: z.enum(['config', 'logs']).optional().describe('config (default) or logs.'), | ||
| }, | ||
@@ -50,7 +47,7 @@ annotations: { readOnlyHint: true }, | ||
| description: 'Manage a webhook subscription. Pick ONE action with `action`: ' + | ||
| '`create` — pass `fields` with `name`, `url` (the HTTPS endpoint receiving deliveries) and `events` (the event subscription object — see list_reference_data type webhook_event_types); the API returns a signing secret ONCE on creation — store it to verify incoming payloads. ' + | ||
| '`create` — `fields`: `name`, `url` (the HTTPS endpoint receiving deliveries), `events` (see list_reference_data type webhook_event_types); the signing secret is returned ONCE on creation — store it to verify payloads. ' + | ||
| '`update` — supply only the fields to change in `fields`: name, url, events, or is_active (false pauses deliveries). ' + | ||
| '`delete` — permanently removes the subscription; it stops receiving events. ' + | ||
| '`test` — sends a test event to confirm reachability and signature verification; safe to repeat. ' + | ||
| '`rotate_secret` — generates and returns a new signing secret — WARNING: the old secret stops working immediately; update your endpoint right away or deliveries will fail verification. ' + | ||
| '`delete` — permanently removes the subscription. ' + | ||
| '`test` — sends a test event to confirm reachability and signature verification. ' + | ||
| '`rotate_secret` — returns a new signing secret — WARNING: the old secret stops working immediately; update your endpoint or deliveries fail verification. ' + | ||
| '`webhook_id` is required for every action except create.', | ||
@@ -65,3 +62,3 @@ inputShape: { | ||
| .optional() | ||
| .describe('The webhook attributes (create: name, url, events; update: any of those plus is_active), forwarded verbatim.'), | ||
| .describe('The webhook attributes, forwarded verbatim.'), | ||
| }, | ||
@@ -68,0 +65,0 @@ annotations: { destructiveHint: true }, |
+2
-2
| { | ||
| "name": "@labelgrid/mcp", | ||
| "version": "0.5.0", | ||
| "version": "0.6.0", | ||
| "mcpName": "io.github.labelgrid/labelgrid-mcp", | ||
@@ -30,3 +30,3 @@ "description": "Official LabelGrid MCP server — connect your AI client to your LabelGrid account", | ||
| "dependencies": { | ||
| "@labelgrid/core": "0.2.0", | ||
| "@labelgrid/core": "0.2.1", | ||
| "@modelcontextprotocol/sdk": "^1.12.0", | ||
@@ -33,0 +33,0 @@ "zod": "^3.24.0" |
+14
-12
@@ -107,3 +107,3 @@ # LabelGrid MCP Server | ||
| _31 tools across 8 toolsets. This table is generated from the | ||
| _33 tools across 8 toolsets. This table is generated from the | ||
| tool definitions by `npm run gen-docs` — do not edit it by hand._ | ||
@@ -116,2 +116,3 @@ | ||
| | `get_account` | read | Read the authenticated LabelGrid account. Pick ONE view with `view`: `profile` returns the account profile — including the release submission limit/quota and terms-acceptance status — use it to confirm which account your API token belongs to before making other calls; `balance` returns your accounting summary — current balance and related account-level financial totals. | | ||
| | `get_rate_limit` | read | The account’s API rate budget per category (read, write, export, analytics): ceiling, remaining, and window reset. Every token on the account shares one budget; a null ceiling means none applies. Free to call — use it to pace requests and to recover from a 429. | | ||
| | `revoke_api_token` | write | Revoke a LabelGrid API token. Pass token_id to revoke a specific token; omit it to revoke the token currently in use. WARNING: revoking the current token immediately ends this session — the server loses access and stops working until you configure a new token. | | ||
@@ -123,3 +124,3 @@ | ||
| | --- | --- | --- | | ||
| | `list_reference_data` | read | Fetch a LabelGrid reference dataset used to resolve the IDs and codes the catalog and release tools expect. Pick ONE dataset with `type`: `genres` and `genre_categories` (genre IDs), `languages` (audio/metadata language codes), `contributor_roles`, `instruments`, `distro_outlets` (the outlets/stores available to your account), `territories` (country codes), `issue_definitions` (each review issue code’s title, description, severity and whether it blocks distribution; codes are string slugs), or `webhook_event_types` (every webhook event type with its payload schema). Call this when you need a valid ID or code. The same datasets are exposed as MCP resources at labelgrid://reference/{type}; this tool is the fallback for clients that don’t surface resources. | | ||
| | `list_reference_data` | read | Fetch a LabelGrid reference dataset used to resolve the IDs and codes the catalog and release tools expect. Pick ONE dataset with `type`: `genres` and `genre_categories` (genre IDs), `languages` (audio/metadata language codes), `contributor_roles`, `instruments`, `distro_outlets` (the outlets/stores available to you), `territories` (country codes), `issue_definitions` (review issue codes — string slugs — with severity and whether they block distribution), or `webhook_event_types` (event types with their payload schemas). Also exposed as MCP resources at labelgrid://reference/{type}; this tool is the fallback. | | ||
@@ -130,9 +131,9 @@ ### Catalog (labels, artists, writers, publishers, releases, tracks) `catalog` | ||
| | --- | --- | --- | | ||
| | `search_catalog` | read | List catalog entities of one kind, paginated. `filters` takes the endpoint’s own filter names, passed through verbatim — label: no documented filters — paginate with page/per_page. artist: artist_name (filter by artist name). writer: name (writer name), ipi (IPI number). publisher: name (publisher name), ipi (IPI number). release: label_id (owning label id), is_live (1 = live/distributed only), barcode_number (UPC/EAN), cat (catalog number). track: release_id (one release’s tracks), isrc (filter by ISRC). Use get_catalog_item for full detail. | | ||
| | `search_catalog` | read | List catalog entities of one kind, paginated. `filters` takes the endpoint’s own filter names, passed through verbatim — label: no documented filters. artist: artist_name. writer: name, ipi. publisher: name, ipi. release: label_id, is_live (1 = live only), barcode_number (UPC/EAN), cat. track: release_id, isrc. Use get_catalog_item for full detail. | | ||
| | `get_catalog_item` | read | Retrieve one catalog entity by id, with full detail (e.g. a release’s metadata and track listing, a track’s contributors and royalty splits, a writer’s PRO/IPI). | | ||
| | `create_catalog_item` | write | Create a catalog entity: pass its attributes in `fields` — the API owns all validation. Required and common fields per entity: label — required: name, default_email; optional: support email, website/platform URLs, default copyright lines, isrc_base. artist — required: artist_name; optional: full_name, email, location, bios, isni, default_language, platform profile URLs. writer — required: first_name, last_name; optional: middle_name, display_credits, email, country, pro, ipi, isni, publisher_id (or publisher_name/publisher_pro/publisher_ipi). publisher — required: name; optional: ipi, pro, isni, controlled_publisher. release — required on create: content_type, label_id, artists, titles, cat (catalog number), artwork_ai_usage, primary_genre_id; many optional fields (dates, copyright lines, genres, per-outlet URLs). Once submitted or distributed some fields are locked — changing one returns a 403 with code RELEASE_LOCKED_FIELDS naming exactly which fields cannot change. track — required on create: release_id, disc, track_num, composition_type, artists, audio_ai_usage, composition_ai_usage, commercial_samples, audio_language, contributors, and recording_country (ISO 3166-1 alpha-2, e.g. "US"); optional: titles, isrc, iswc, writers, publishers, splits, and more. A release is created in DRAFT state — add tracks, then run the release checks before distributing. | | ||
| | `create_catalog_item` | write | Create a catalog entity: pass its attributes in `fields` — the API owns all validation. Required and common fields per entity: label — required: name, default_email; optional: support email, website/platform URLs, default copyright lines, isrc_base. artist — required: artist_name; optional: full_name, email, location, bios, isni, default_language, platform profile URLs. writer — required: first_name, last_name; optional: middle_name, display_credits, email, country, pro, ipi, isni, publisher_id (or publisher_name/publisher_pro/publisher_ipi). publisher — required: name; optional: ipi, pro, isni, controlled_publisher. release — required on create: content_type, label_id, artists, titles, cat (catalog number), artwork_ai_usage, primary_genre_id; many optional fields (dates, copyright lines, genres, per-outlet URLs). track — required on create: release_id, disc, track_num, composition_type, artists, audio_ai_usage, composition_ai_usage, commercial_samples, audio_language, contributors, and recording_country (ISO 3166-1 alpha-2, e.g. "US"); optional: titles, isrc, iswc, writers, publishers, splits, and more. A release is created in DRAFT state — add tracks, then run the release checks before distributing. | | ||
| | `update_catalog_item` | write | Update a catalog entity: supply only the fields to change in `fields` (same field sets as create_catalog_item). Once a release is submitted or distributed, some release and track fields are locked — changing one returns a 403 with code RELEASE_LOCKED_FIELDS naming exactly which fields cannot change. | | ||
| | `delete_catalog_item` | write | Delete a catalog entity. The API refuses deletes that would orphan data — label: refused while the label still has releases — remove or reassign its releases first. artist: refused while still referenced by releases or tracks. writer: refused while still referenced by tracks. publisher: refused while still referenced by writers. release: only a never-submitted draft can be deleted. track: allowed while the parent release is an editable draft; refused once submitted or distributed. | | ||
| | `delete_catalog_item` | write | Delete a catalog entity. The API refuses deletes that would orphan data — label: refused while the label still has releases — remove or reassign its releases first. artist: refused while still referenced by releases or tracks. writer: refused while still referenced by tracks. publisher: refused while still referenced by writers. release: only a never-submitted draft can be deleted. track: refused once the release is no longer an editable draft. | | ||
| | `upload_image` | write | Upload a label image (logo, dark-mode logo, or background) or an artist photo from a local image file, per `target`. | | ||
| | `get_asset` | read | Read a track or release asset. Valid combinations: (1) mode='info' + parent='track' + asset stereo\|dolby\|lyrics — file metadata (not the bytes) incl. processing state. (2) mode='info' + parent='release' + asset square\|tall — motion-artwork (animated cover) video metadata. (3) mode='download_url' + parent='track' + asset audio_16\|audio_24\|audio_32 (WAV master) or audio_preview_full\|audio_preview_clip (MP3 preview) — returns { download_url, expires_in }, a signed URL that expires roughly 10 minutes after issue; fetch it directly — do not send your API token to it. Any other combination returns a structured error. | | ||
| | `get_asset` | read | Read a track or release asset. Valid combinations: (1) mode='info' + parent='track' + asset stereo\|dolby\|lyrics — file metadata (not the bytes) incl. processing state. (2) mode='info' + parent='release' + asset square\|tall — motion-artwork (animated cover) video metadata. (3) mode='download_url' + parent='track' + asset audio_16\|audio_24\|audio_32 (WAV master) or audio_preview_full\|audio_preview_clip (MP3 preview) — returns { download_url, expires_in }, a signed URL that expires roughly 10 minutes after issue; fetch it directly — do not send your API token to it. Any other combination is refused. | | ||
@@ -155,4 +156,5 @@ ### Releases (review, delivery, links, licenses, checks) `releases` | ||
| | --- | --- | --- | | ||
| | `get_analytics` | read | Streaming analytics summary. Window capped at 400 days; `metrics` takes 1-12 section keys per request (split larger selections — responses are cached). KUGOU/KUWO/QQMUSIC report weekly: one point per week carrying the whole week — never average it per day. `meta` carries `platform_cadence`, `section_granularity`, `sections_as_of` and `sections_complete_through` (later dates still filling in). Call get_analytics_availability first for section-per-platform support. Rate-limited ~60/min; windows over 90 days draw a separate lower ~30/min budget — prefer shorter windows for polling. A 429 carries retry_after_seconds. | | ||
| | `get_analytics` | read | Streaming analytics summary. Window capped at 400 days; `metrics` takes 1-12 section keys per request (split larger selections — responses are cached). KUGOU/KUWO/QQMUSIC report weekly: one point per week carrying the whole week — never average it per day. `meta` carries `platform_cadence`, `section_granularity`, `sections_as_of` and `sections_complete_through` (later dates still filling in). Call get_analytics_availability first for section-per-platform support. The `social-*` / `soundcloud-engagement` sections cover social and UGC usage instead of streaming: their `platform` is a UGC platform; a use, view and play are distinct quantities, never summed with each other or with streams; `ugc_platform` narrows them. Selecting any adds `meta.social_availability` (which UGC platforms report each signal) — the streaming matrix excludes them. The `track-*-daily` sections need a `release_id`, `isrc` or `upc` scope. `track-listeners-daily` sums per-entry daily counts: not distinct people, not summable across dates. Rate-limited ~60/min; windows over 90 days draw a separate lower ~30/min budget — prefer shorter windows for polling. A 429 carries retry_after_seconds. | | ||
| | `get_analytics_availability` | read | Static `availability` matrix (per section, per platform) plus `platform_cadence` (daily\|weekly per platform). Account- and date-independent: fetch once, reuse. Read it before get_analytics so an unreported section is treated as unavailable, not an empty chart. | | ||
| | `get_analytics_rankings` | read | Top-N rankings for a window, ordered by summed streams. Pick ONE `view`: `leaderboards` — your top artists, tracks or albums (`type` required; `all` returns all three in one request). `placements` — the playlists and radio containers driving streams, summed across storefronts. Same scope filters as get_analytics; `limit` 1-50 (default 10). Under a `platform` filter, an `availability` of `not_available_for_platform` means that platform reports no ranking and `data` is empty. | | ||
| | `query_artificial_streaming` | read | Artificial-streaming (streaming-integrity) reads. Pick ONE `view`: `flags` — Stream Radar early-warning flags, paginated (`filters`: status, severity, dsp, isrc, release_id, detected_from/detected_to). Stream Radar is an optional add-on; without it the API returns a 403, surfaced verbatim. `flag_detail` — one flag by `flag_id`. `records` — reported artificial-streaming records, cursor-paginated; the detail behind any artificial-streaming fee (`filters`: dsp, start_date/end_date, release_id, isrc). `fee_breakdown` — per-release fee breakdown for one `period` (YYYY-MM). response_format:'detailed' returns the verbatim API response. | | ||
@@ -164,4 +166,4 @@ | ||
| | --- | --- | --- | | ||
| | `query_financials` | read | Query your financial data. Pick ONE view with `view`: `statements` lists your royalty statements, paginated — `filters`: label_id, release_id, isrc, upc, start_date/end_date; group_by="release" rolls totals up per release. `statement_detail` retrieves one statement by `invoice_number` (required). `transactions` lists account transactions, paginated — same `filters`; sort with `sort`; group_by="release" rolls up per release. `royalty_breakdown` returns a cursor-paginated royalty breakdown — `group_by` is REQUIRED for this view: a comma-separated, ordered subset of: track, dsp, release, territory, period (e.g. "release,dsp"); same `filters`; pass `cursor` to page. Use download_statement for statement line items (CSV) or the invoice PDF. response_format:'detailed' returns the verbatim API response. | | ||
| | `download_statement` | read | Download statement files. `format: 'csv'` downloads statement line items — pass invoice_number for one statement, OR a start_date/end_date range to export across statements; with save_to_path (an absolute path whose parent directory exists) the CSV is written there and the byte count returned; otherwise it is returned inline, truncated at 100KB (truncated: true) — use save_to_path for large exports. `format: 'invoice_pdf'` downloads the invoice PDF — invoice_number and save_to_path are both REQUIRED (the PDF is binary). An existing file is never overwritten (returns FILE_EXISTS). | | ||
| | `query_financials` | read | Query your financial data. Pick ONE view with `view`: `statements` lists your royalty statements, paginated — `filters`: label_id, release_id, isrc, upc, start_date/end_date; group_by="release" rolls totals up per release. `statement_detail` retrieves one statement by `invoice_number` (required). `transactions` lists account transactions, paginated — same `filters` and `group_by`; sort with `sort`. `royalty_breakdown` returns a cursor-paginated royalty breakdown — `group_by` is REQUIRED for this view: a comma-separated, ordered subset of: track, dsp, release, territory, period (e.g. "release,dsp"); same `filters`; pass `cursor` to page. Use download_statement for statement line items (CSV) or the invoice PDF. response_format:'detailed' returns the verbatim API response. | | ||
| | `download_statement` | read | Download statement files. `format: 'csv'` downloads statement line items — pass invoice_number for one statement, OR a start_date/end_date range to export across statements; with save_to_path the CSV is written there and the byte count returned; otherwise it is returned inline, truncated at 100KB (truncated: true) — use save_to_path for large exports. `format: 'invoice_pdf'` downloads the invoice PDF — invoice_number and save_to_path are both REQUIRED (the PDF is binary). An existing file is never overwritten (returns FILE_EXISTS). | | ||
@@ -172,4 +174,4 @@ ### Webhooks (off by default — enable via LABELGRID_TOOLSETS) `webhooks` | ||
| | --- | --- | --- | | ||
| | `list_webhooks` | read | Read your webhook subscriptions. `view: 'config'` (the default) lists the webhook subscriptions configured on your account — each with its URL, subscribed events and active state — or retrieves one subscription when `webhook_id` is given. `view: 'logs'` retrieves the recent delivery log for a webhook (`webhook_id` required) — attempts, response codes and outcomes — to debug why events did or did not reach your endpoint. | | ||
| | `manage_webhook` | write | Manage a webhook subscription. Pick ONE action with `action`: `create` — pass `fields` with `name`, `url` (the HTTPS endpoint receiving deliveries) and `events` (the event subscription object — see list_reference_data type webhook_event_types); the API returns a signing secret ONCE on creation — store it to verify incoming payloads. `update` — supply only the fields to change in `fields`: name, url, events, or is_active (false pauses deliveries). `delete` — permanently removes the subscription; it stops receiving events. `test` — sends a test event to confirm reachability and signature verification; safe to repeat. `rotate_secret` — generates and returns a new signing secret — WARNING: the old secret stops working immediately; update your endpoint right away or deliveries will fail verification. `webhook_id` is required for every action except create. | | ||
| | `list_webhooks` | read | Read your webhook subscriptions. `view: 'config'` (the default) lists them — URL, subscribed events, active state — or retrieves one when `webhook_id` is given. `view: 'logs'` retrieves the recent delivery log for a webhook (`webhook_id` required) — attempts, response codes and outcomes — to debug why events did or did not reach your endpoint. | | ||
| | `manage_webhook` | write | Manage a webhook subscription. Pick ONE action with `action`: `create` — `fields`: `name`, `url` (the HTTPS endpoint receiving deliveries), `events` (see list_reference_data type webhook_event_types); the signing secret is returned ONCE on creation — store it to verify payloads. `update` — supply only the fields to change in `fields`: name, url, events, or is_active (false pauses deliveries). `delete` — permanently removes the subscription. `test` — sends a test event to confirm reachability and signature verification. `rotate_secret` — returns a new signing secret — WARNING: the old secret stops working immediately; update your endpoint or deliveries fail verification. `webhook_id` is required for every action except create. | | ||
@@ -180,3 +182,3 @@ ### Distribution (full writes) `distribution` | ||
| | --- | --- | --- | | ||
| | `upload_asset` | full-write | Upload a finalized track or release asset from a local file. `id` is the track id for track_* targets, the release id for release_*. `track_stereo` (WAV/FLAC/AIFF), `track_dolby` (Dolby Atmos WAV) and `track_lyrics` (LRC) process asynchronously — check state with get_asset (mode info). `release_cover_art` uploads or replaces the static cover art image. `release_motion_square` / `release_motion_tall` upload the square or tall animated cover (motion artwork) video, also asynchronous. All assets become immutable once the release is distributed — upload final files first. | | ||
| | `upload_asset` | full-write | Upload a finalized track or release asset from a local file. `track_stereo` (WAV/FLAC/AIFF), `track_dolby` (Dolby Atmos WAV) and `track_lyrics` (LRC) process asynchronously — check state with get_asset (mode info). `release_cover_art` uploads or replaces the static cover art image. `release_motion_square`/`release_motion_tall` upload the animated cover (motion artwork) video, also asynchronous. All assets become immutable once the release is distributed — upload final files first. | | ||
| | `delete_asset` | full-write | Delete a track asset (track_*) or an animated cover / motion artwork video (release_motion_*). Allowed only while the parent release is an editable draft; refused once locked or distributed. Cover art cannot be deleted. | | ||
@@ -183,0 +185,0 @@ | `manage_track_license` | full-write | Manage license documents on a track (cover or cleared sample). `upload` attaches a new license — `file_path` required, `type` selects cover/sample; optional metadata fields. `update` replaces the file and/or metadata — `track_license_id` (from list_track_licenses) and `file_path` required. `delete` permanently removes a license and its file — `track_license_id` required; cannot be undone. Immutability-governed once the release is live. | |
+2
-2
@@ -5,3 +5,3 @@ { | ||
| "description": "Official LabelGrid MCP server — manage your music catalog, releases, analytics and distribution.", | ||
| "version": "0.4.0", | ||
| "version": "0.6.0", | ||
| "websiteUrl": "https://labelgrid.com", | ||
@@ -16,3 +16,3 @@ "repository": { | ||
| "identifier": "@labelgrid/mcp", | ||
| "version": "0.4.0", | ||
| "version": "0.6.0", | ||
| "transport": { | ||
@@ -19,0 +19,0 @@ "type": "stdio" |
178688
3.87%2969
4.18%350
0.57%+ Added
- Removed
Updated