@labelgrid/mcp 0.5.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -5,6 +5,42 @@ All notable changes to `@labelgrid/mcp` are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.0] - 2026-08-05
9
+
10
+ ### Added
11
+
12
+ - `get_analytics_rankings` — top-N rankings for a window, ordered by summed
13
+ streams. `view: 'leaderboards'` ranks top artists, tracks or albums (`type`
14
+ required); `view: 'placements'` ranks the playlists and radio containers
15
+ driving streams.
16
+ - `get_rate_limit` — the account's API rate budget per category, with the
17
+ ceiling, the remaining allowance and the window reset.
18
+ - 10 new analytics section keys (47 total): eight social and UGC sections
19
+ (`social-usage-over-time`, `social-reach-over-time`, `social-platform-mix`,
20
+ `social-top-tracks`, `social-territory`, `social-artist-reach`,
21
+ `social-artist-reach-daily`, `soundcloud-engagement`) and two per-track daily
22
+ series (`track-streams-daily`, `track-listeners-daily`, which need a
23
+ `release_id`, `isrc` or `upc` scope).
24
+ - `ugc_platform` filter on `get_analytics`, narrowing the social and UGC
25
+ sections.
26
+ - `AMAZON` as a `platform` filter value on `get_analytics` and
27
+ `get_analytics_rankings` (11 total), matching the platforms the API accepts.
28
+ - `query_financials` concise mode now returns the fee fields — `labelgrid_fee`,
29
+ `platform_fee_usd`, `ugc_fee_usd`, `labelgrid_rate` and `labelgrid_ugc_rate`.
30
+ Previously a fee was visible only with `response_format: 'detailed'`.
31
+
32
+ ### Changed
33
+
34
+ - Social and UGC sections report their own per-section availability in
35
+ `meta.social_availability`; the streaming availability matrix does not cover
36
+ them. A use, a view and a play are distinct quantities and are never summed
37
+ with each other or with streams.
38
+ - `track-listeners-daily` sums each platform track entry's daily count. Where a
39
+ track exists as more than one entry it is not a distinct count of people, and
40
+ it is not summable across dates.
41
+ - Tool descriptions across the catalog were tightened. No tool names,
42
+ parameters or behavior changed beyond the items above.
43
+
8
44
  ## [0.5.0] - 2026-07-27
9
45
 
10
46
  ### Added
package/README.md CHANGED
@@ -104,7 +104,7 @@ The nine reference datasets are also exposed as MCP **resources** at `labelgrid:
104
104
 
105
105
  <!-- TOOLS:BEGIN -->
106
106
 
107
- _31 tools across 8 toolsets. This table is generated from the
107
+ _33 tools across 8 toolsets. This table is generated from the
108
108
  tool definitions by `npm run gen-docs` — do not edit it by hand._
109
109
 
110
110
  ### Account `account`
@@ -112,25 +112,26 @@ tool definitions by `npm run gen-docs` — do not edit it by hand._
112
112
  | Tool | Gate | Description |
113
113
  | --- | --- | --- |
114
114
  | `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. |
115
+ | `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. |
115
116
  | `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. |
116
117
 
117
118
  ### Reference data `reference`
118
119
 
119
120
  | Tool | Gate | Description |
120
121
  | --- | --- | --- |
121
- | `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. |
122
+ | `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. |
122
123
 
123
124
  ### Catalog (labels, artists, writers, publishers, releases, tracks) `catalog`
124
125
 
125
126
  | Tool | Gate | Description |
126
127
  | --- | --- | --- |
127
- | `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. |
128
+ | `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. |
128
129
  | `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). |
129
- | `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. |
130
+ | `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. |
130
131
  | `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. |
131
- | `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. |
132
+ | `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. |
132
133
  | `upload_image` | write | Upload a label image (logo, dark-mode logo, or background) or an artist photo from a local image file, per `target`. |
133
- | `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. |
134
+ | `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. |
134
135
 
135
136
  ### Releases (review, delivery, links, licenses, checks) `releases`
136
137
 
@@ -148,29 +149,30 @@ tool definitions by `npm run gen-docs` — do not edit it by hand._
148
149
 
149
150
  | Tool | Gate | Description |
150
151
  | --- | --- | --- |
151
- | `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. |
152
+ | `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. |
152
153
  | `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. |
154
+ | `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. |
153
155
  | `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. |
154
156
 
155
157
  ### Finance (statements, transactions, royalties) `finance`
156
158
 
157
159
  | Tool | Gate | Description |
158
160
  | --- | --- | --- |
159
- | `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. |
160
- | `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). |
161
+ | `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. |
162
+ | `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). |
161
163
 
162
164
  ### Webhooks (off by default — enable via LABELGRID_TOOLSETS) `webhooks`
163
165
 
164
166
  | Tool | Gate | Description |
165
167
  | --- | --- | --- |
166
- | `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. |
167
- | `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. |
168
+ | `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. |
169
+ | `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. |
168
170
 
169
171
  ### Distribution (full writes) `distribution`
170
172
 
171
173
  | Tool | Gate | Description |
172
174
  | --- | --- | --- |
173
- | `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. |
175
+ | `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. |
174
176
  | `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. |
175
177
  | `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. |
176
178
  | `distribute_release` | full-write | Submit a release to the stores/outlets — the FINAL action that sends it out; run_release_checks (check validate) should pass first. The server enforces the account’s weekly submission limit. Reuse the SAME idempotency_key when retrying an unobserved call; without one each call is a new submission. |
package/dist/coverage.js CHANGED
@@ -17,6 +17,7 @@
17
17
  export const COVERAGE = {
18
18
  // account
19
19
  'GET /me': 'get_account',
20
+ 'GET /rate-limit': 'get_rate_limit',
20
21
  'DELETE /tokens/current': 'revoke_api_token',
21
22
  'DELETE /tokens/{tokenId}': 'revoke_api_token',
22
23
  // reference
@@ -30,6 +31,8 @@ export const COVERAGE = {
30
31
  // insights
31
32
  'GET /analytics/summary': 'get_analytics',
32
33
  'GET /analytics/availability': 'get_analytics_availability',
34
+ 'GET /analytics/leaderboards': 'get_analytics_rankings',
35
+ 'GET /analytics/placements': 'get_analytics_rankings',
33
36
  // catalog reads
34
37
  'GET /labels': 'search_catalog',
35
38
  'GET /labels/{label}': 'get_catalog_item',
@@ -72,6 +72,11 @@ export const CONCISE_ALLOWLISTS = {
72
72
  'status',
73
73
  'currency',
74
74
  'gross_usd',
75
+ 'labelgrid_fee',
76
+ 'platform_fee_usd',
77
+ 'ugc_fee_usd',
78
+ 'labelgrid_rate',
79
+ 'labelgrid_ugc_rate',
75
80
  'net_usd',
76
81
  'amount',
77
82
  'total_due_usd',
@@ -1,3 +1,6 @@
1
- /** Account toolset: read the authenticated account, revoke API tokens. */
1
+ /**
2
+ * Account toolset: read the authenticated account, read its rate budget, revoke
3
+ * API tokens.
4
+ */
2
5
  import type { ToolDef } from './types.js';
3
6
  export declare const accountTools: ToolDef[];
@@ -1,4 +1,7 @@
1
- /** Account toolset: read the authenticated account, revoke API tokens. */
1
+ /**
2
+ * Account toolset: read the authenticated account, read its rate budget, revoke
3
+ * API tokens.
4
+ */
2
5
  import { z } from 'zod';
3
6
  const getAccount = {
4
7
  name: 'get_account',
@@ -14,6 +17,17 @@ const getAccount = {
14
17
  annotations: { readOnlyHint: true },
15
18
  handler: (args, { client }) => client.get(args.view === 'balance' ? '/account' : '/me'),
16
19
  };
20
+ const getRateLimit = {
21
+ name: 'get_rate_limit',
22
+ toolset: 'account',
23
+ gate: 'read',
24
+ title: 'Get the rate budget',
25
+ description: 'The account’s API rate budget per category (read, write, export, analytics): ceiling, remaining, and window reset. ' +
26
+ '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.',
27
+ inputShape: {},
28
+ annotations: { readOnlyHint: true },
29
+ handler: (_args, { client }) => client.get('/rate-limit'),
30
+ };
17
31
  const revokeApiToken = {
18
32
  name: 'revoke_api_token',
19
33
  toolset: 'account',
@@ -29,4 +43,4 @@ const revokeApiToken = {
29
43
  : client.delete(`/tokens/${tokenId}`);
30
44
  },
31
45
  };
32
- export const accountTools = [getAccount, revokeApiToken];
46
+ export const accountTools = [getAccount, getRateLimit, revokeApiToken];
@@ -21,7 +21,7 @@ const idArg = z.number().int().positive().describe('The entity id.');
21
21
  const responseFormat = z
22
22
  .enum(['concise', 'detailed'])
23
23
  .optional()
24
- .describe("'concise' (default) keeps high-signal fields (ids always kept); 'detailed' returns the verbatim API response.");
24
+ .describe("'concise' (default) keeps high-signal fields and ids; 'detailed' is the verbatim response.");
25
25
  /** A permissive body of API fields, forwarded verbatim to the endpoint. */
26
26
  function fieldsBody(desc) {
27
27
  return z.record(z.string(), z.unknown()).describe(desc);
@@ -204,7 +204,7 @@ const getAsset = {
204
204
  "(1) mode='info' + parent='track' + asset stereo|dolby|lyrics — file metadata (not the bytes) incl. processing state. " +
205
205
  "(2) mode='info' + parent='release' + asset square|tall — motion-artwork (animated cover) video metadata. " +
206
206
  "(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. " +
207
- 'Any other combination returns a structured error.',
207
+ 'Any other combination is refused.',
208
208
  inputShape: {
209
209
  parent: z.enum(['track', 'release']).describe('Whose asset.'),
210
210
  id: z.number().int().positive().describe('The track id or release id, per `parent`.'),
@@ -221,7 +221,7 @@ const getAsset = {
221
221
  'audio_preview_full',
222
222
  'audio_preview_clip',
223
223
  ])
224
- .describe('Which asset — see the description for the valid parent/mode pairings.'),
224
+ .describe('Which asset — see the valid combinations above.'),
225
225
  mode: z
226
226
  .enum(['info', 'download_url'])
227
227
  .optional()
@@ -46,8 +46,8 @@ const licenseMeta = {
46
46
  .enum(['licensing_agency', 'direct_from_publisher'])
47
47
  .optional()
48
48
  .describe('Where the license came from.'),
49
- license_provider_name: z.string().optional().describe('The name of the license provider.'),
50
- original_track_link: z.string().optional().describe('URL to the original/source track.'),
49
+ license_provider_name: z.string().optional(),
50
+ original_track_link: z.string().optional().describe('URL to the source track.'),
51
51
  };
52
52
  /** Collects the defined license metadata fields into a string map for multipart. */
53
53
  function licenseExtra(args) {
@@ -70,10 +70,10 @@ const uploadAsset = {
70
70
  toolset: 'distribution',
71
71
  gate: 'full_write',
72
72
  title: 'Upload a release/track asset',
73
- 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_*. ' +
73
+ description: 'Upload a finalized track or release asset from a local file. ' +
74
74
  '`track_stereo` (WAV/FLAC/AIFF), `track_dolby` (Dolby Atmos WAV) and `track_lyrics` (LRC) process asynchronously — check state with get_asset (mode info). ' +
75
75
  '`release_cover_art` uploads or replaces the static cover art image. ' +
76
- '`release_motion_square` / `release_motion_tall` upload the square or tall animated cover (motion artwork) video, also asynchronous. ' +
76
+ '`release_motion_square`/`release_motion_tall` upload the animated cover (motion artwork) video, also asynchronous. ' +
77
77
  'All assets become immutable once the release is distributed — upload final files first.',
78
78
  inputShape: {
79
79
  target: z
@@ -86,7 +86,7 @@ const uploadAsset = {
86
86
  'release_motion_tall',
87
87
  ])
88
88
  .describe('Which asset to upload.'),
89
- id: z.number().int().positive().describe('The track id (track_*) or release id (release_*).'),
89
+ id: z.number().int().positive().describe('The track or release id, per `target`.'),
90
90
  file_path: z.string().describe('Local path to the file to upload.'),
91
91
  },
92
92
  annotations: {},
@@ -170,7 +170,7 @@ const manageTrackLicense = {
170
170
  .int()
171
171
  .positive()
172
172
  .optional()
173
- .describe('From list_track_licenses. Required for update/delete.'),
173
+ .describe('Required for update/delete.'),
174
174
  file_path: z
175
175
  .string()
176
176
  .optional()
@@ -295,7 +295,7 @@ const queryFinancials = {
295
295
  description: 'Query your financial data. Pick ONE view with `view`: ' +
296
296
  '`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. ' +
297
297
  '`statement_detail` retrieves one statement by `invoice_number` (required). ' +
298
- '`transactions` lists account transactions, paginated — same `filters`; sort with `sort`; group_by="release" rolls up per release. ' +
298
+ '`transactions` lists account transactions, paginated — same `filters` and `group_by`; sort with `sort`. ' +
299
299
  '`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. ' +
300
300
  "Use download_statement for statement line items (CSV) or the invoice PDF. response_format:'detailed' returns the verbatim API response.",
301
301
  inputShape: {
@@ -306,19 +306,19 @@ const queryFinancials = {
306
306
  group_by: z
307
307
  .string()
308
308
  .optional()
309
- .describe('REQUIRED for royalty_breakdown (ordered subset: track, dsp, release, territory, period); "release" rolls statements/transactions up per release.'),
309
+ .describe('Required for royalty_breakdown; "release" rolls statements/transactions up per release.'),
310
310
  sort: z.string().optional().describe('Sort expression (view transactions).'),
311
311
  filters: z
312
312
  .record(z.string(), z.unknown())
313
313
  .optional()
314
- .describe('label_id, release_id, isrc, upc, start_date, end_date — passed through verbatim.'),
314
+ .describe('Filter names → values, passed through verbatim.'),
315
315
  cursor: z.string().optional().describe('Pagination cursor (view royalty_breakdown).'),
316
316
  page: z.number().int().positive().optional().describe('1-based page number.'),
317
317
  per_page: z.number().int().positive().optional().describe('Items per page.'),
318
318
  response_format: z
319
319
  .enum(['concise', 'detailed'])
320
320
  .optional()
321
- .describe("'concise' (default) keeps only the high-signal fields (ids always kept); 'detailed' returns the verbatim API response."),
321
+ .describe("'concise' (default) or 'detailed'."),
322
322
  },
323
323
  annotations: { readOnlyHint: true },
324
324
  handler: async (args, { client }) => {
@@ -369,11 +369,9 @@ const downloadStatement = {
369
369
  toolset: 'finance',
370
370
  gate: 'read',
371
371
  title: 'Download a statement file',
372
- 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).",
372
+ 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).",
373
373
  inputShape: {
374
- format: z
375
- .enum(['csv', 'invoice_pdf'])
376
- .describe('Which file: csv (line items) or invoice_pdf (the invoice PDF).'),
374
+ format: z.enum(['csv', 'invoice_pdf']).describe('Which file.'),
377
375
  invoice_number: z
378
376
  .string()
379
377
  .optional()
@@ -383,7 +381,7 @@ const downloadStatement = {
383
381
  save_to_path: z
384
382
  .string()
385
383
  .optional()
386
- .describe('Absolute path (existing parent dir) to write the file to. Optional for csv (otherwise returned inline); required for invoice_pdf.'),
384
+ .describe('Absolute path (its parent directory must exist) to write the file to.'),
387
385
  },
388
386
  annotations: { readOnlyHint: true },
389
387
  handler: async (args, { client, config }) => {
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Insights toolset: the streaming analytics summary, the availability-discovery
3
- * endpoint, and the consolidated artificial-streaming query (early-warning
4
- * flags, reported records, and the fee breakdown). All read-only.
3
+ * endpoint, the top-N rankings (leaderboards and placements), and the
4
+ * consolidated artificial-streaming query (early-warning flags, reported
5
+ * records, and the fee breakdown). All read-only.
5
6
  */
6
7
  import type { ToolDef } from './types.js';
7
8
  export declare const insightsTools: ToolDef[];
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Insights toolset: the streaming analytics summary, the availability-discovery
3
- * endpoint, and the consolidated artificial-streaming query (early-warning
4
- * flags, reported records, and the fee breakdown). All read-only.
3
+ * endpoint, the top-N rankings (leaderboards and placements), and the
4
+ * consolidated artificial-streaming query (early-warning flags, reported
5
+ * records, and the fee breakdown). All read-only.
5
6
  */
6
7
  import { z } from 'zod';
7
8
  import { applyProjection } from '../projection.js';
8
9
  /**
9
- * The 37 metric sections the summary endpoint can return, in the server's
10
- * canonical order.
10
+ * The 47 metric sections the summary endpoint can return, in the server's
11
+ * canonical order: the streaming sections first, then the social and UGC
12
+ * family, then the per-track daily series.
11
13
  */
12
14
  const METRICS = [
13
15
  'streams',
@@ -47,6 +49,31 @@ const METRICS = [
47
49
  'hour-of-day',
48
50
  'shazams-by-city',
49
51
  'shazams-by-state',
52
+ 'social-usage-over-time',
53
+ 'social-reach-over-time',
54
+ 'social-platform-mix',
55
+ 'social-top-tracks',
56
+ 'social-territory',
57
+ 'social-artist-reach',
58
+ 'social-artist-reach-daily',
59
+ 'soundcloud-engagement',
60
+ 'track-streams-daily',
61
+ 'track-listeners-daily',
62
+ ];
63
+ /**
64
+ * The UGC platform values `filter[ugc_platform]` accepts. A separate axis from
65
+ * `platform`: it narrows the social and UGC sections only, and its values never
66
+ * appear in a streaming total or platform share.
67
+ */
68
+ const UGC_PLATFORMS = [
69
+ 'snapchat',
70
+ 'instagram',
71
+ 'facebook',
72
+ 'soundcloud',
73
+ 'tiktok',
74
+ 'whatsapp',
75
+ 'threads',
76
+ 'messenger',
50
77
  ];
51
78
  /** The maximum number of section keys the server accepts per summary request. */
52
79
  const MAX_METRICS_PER_REQUEST = 12;
@@ -59,6 +86,7 @@ const PLATFORMS = [
59
86
  'BOOMPLAY',
60
87
  'AWA',
61
88
  'AUDIOMACK',
89
+ 'AMAZON',
62
90
  'KUGOU',
63
91
  'KUWO',
64
92
  'QQMUSIC',
@@ -67,24 +95,30 @@ const getAnalytics = {
67
95
  name: 'get_analytics',
68
96
  toolset: 'insights',
69
97
  gate: 'read',
70
- title: 'Get streaming analytics',
98
+ title: 'Get streaming and social analytics',
71
99
  description: 'Streaming analytics summary. Window capped at 400 days; `metrics` takes 1-12 section keys per request (split larger selections — responses are cached). ' +
72
100
  '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). ' +
73
101
  'Call get_analytics_availability first for section-per-platform support. ' +
102
+ '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. ' +
103
+ '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. ' +
74
104
  '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.',
75
105
  inputShape: {
76
- start_date: z.string().describe('Start of the reporting window, YYYY-MM-DD.'),
77
- end_date: z.string().describe('End of the reporting window, YYYY-MM-DD (max 400-day span).'),
106
+ start_date: z.string().describe('Window start, YYYY-MM-DD.'),
107
+ end_date: z.string().describe('Window end, YYYY-MM-DD.'),
78
108
  metrics: z
79
109
  .array(z.enum(METRICS))
80
110
  .min(1)
81
111
  .max(MAX_METRICS_PER_REQUEST)
82
- .describe('Section keys to return, 1-12 per request.'),
112
+ .describe('Section keys, 1-12 per request.'),
83
113
  platform: z.enum(PLATFORMS).optional(),
114
+ ugc_platform: z
115
+ .enum(UGC_PLATFORMS)
116
+ .optional()
117
+ .describe('Narrows the social/UGC sections only.'),
84
118
  release_id: z.number().int().positive().optional(),
85
119
  isrc: z.string().optional(),
86
120
  upc: z.string().optional(),
87
- artist_names: z.array(z.string()).optional().describe('Filter to one or more artist names.'),
121
+ artist_names: z.array(z.string()).optional(),
88
122
  limit: z.number().int().positive().optional(),
89
123
  },
90
124
  annotations: { readOnlyHint: true },
@@ -93,6 +127,7 @@ const getAnalytics = {
93
127
  start_date: args.start_date,
94
128
  end_date: args.end_date,
95
129
  platform: args.platform,
130
+ ugc_platform: args.ugc_platform,
96
131
  release_id: args.release_id,
97
132
  isrc: args.isrc,
98
133
  upc: args.upc,
@@ -113,6 +148,69 @@ const getAnalyticsAvailability = {
113
148
  annotations: { readOnlyHint: true },
114
149
  handler: (_args, { client }) => client.get('/analytics/availability'),
115
150
  };
151
+ /** The two ranking reads, and the entity kinds a leaderboard can rank. */
152
+ const RANKING_VIEWS = ['leaderboards', 'placements'];
153
+ const LEADERBOARD_TYPES = ['artists', 'tracks', 'albums', 'all'];
154
+ /** Upper bound the ranking endpoints place on `limit`. */
155
+ const MAX_RANKING_LIMIT = 50;
156
+ const getAnalyticsRankings = {
157
+ name: 'get_analytics_rankings',
158
+ toolset: 'insights',
159
+ gate: 'read',
160
+ title: 'Get analytics rankings',
161
+ description: 'Top-N rankings for a window, ordered by summed streams. Pick ONE `view`: ' +
162
+ '`leaderboards` — your top artists, tracks or albums (`type` required; `all` returns all three in one request). ' +
163
+ '`placements` — the playlists and radio containers driving streams, summed across storefronts. ' +
164
+ '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.',
165
+ inputShape: {
166
+ view: z.enum(RANKING_VIEWS).describe('Which ranking read.'),
167
+ start_date: z.string().describe('Window start, YYYY-MM-DD.'),
168
+ end_date: z.string().describe('Window end, YYYY-MM-DD.'),
169
+ type: z.enum(LEADERBOARD_TYPES).optional().describe('Required for view leaderboards.'),
170
+ platform: z.enum(PLATFORMS).optional(),
171
+ ugc_platform: z.enum(UGC_PLATFORMS).optional(),
172
+ release_id: z.number().int().positive().optional(),
173
+ isrc: z.string().optional(),
174
+ upc: z.string().optional(),
175
+ artist_names: z.array(z.string()).optional(),
176
+ label_id: z
177
+ .number()
178
+ .int()
179
+ .positive()
180
+ .optional()
181
+ .describe('Narrow to one of your own labels; it can never widen scope.'),
182
+ limit: z.number().int().positive().max(MAX_RANKING_LIMIT).optional(),
183
+ },
184
+ annotations: { readOnlyHint: true },
185
+ handler: (args, { client }) => {
186
+ const leaderboards = args.view === 'leaderboards';
187
+ if (leaderboards && args.type === undefined) {
188
+ return Promise.resolve({
189
+ error: {
190
+ code: 'INVALID_SELECTOR',
191
+ message: "view 'leaderboards' requires `type` — artists, tracks, albums, or all. `type` does not apply to view 'placements'.",
192
+ status: 0,
193
+ },
194
+ });
195
+ }
196
+ return client.get(leaderboards ? '/analytics/leaderboards' : '/analytics/placements', {
197
+ filter: {
198
+ start_date: args.start_date,
199
+ end_date: args.end_date,
200
+ platform: args.platform,
201
+ ugc_platform: args.ugc_platform,
202
+ release_id: args.release_id,
203
+ isrc: args.isrc,
204
+ upc: args.upc,
205
+ artist_names: args.artist_names,
206
+ label_id: args.label_id,
207
+ },
208
+ // `type` is a leaderboards-only parameter — never sent to placements.
209
+ type: leaderboards ? args.type : undefined,
210
+ limit: args.limit,
211
+ });
212
+ },
213
+ };
116
214
  const queryArtificialStreaming = {
117
215
  name: 'query_artificial_streaming',
118
216
  toolset: 'insights',
@@ -140,7 +238,7 @@ const queryArtificialStreaming = {
140
238
  response_format: z
141
239
  .enum(['concise', 'detailed'])
142
240
  .optional()
143
- .describe("'concise' (default) keeps only the high-signal fields (ids always kept); 'detailed' returns the verbatim API response."),
241
+ .describe("'concise' (default) or 'detailed'."),
144
242
  },
145
243
  annotations: { readOnlyHint: true },
146
244
  handler: async (args, { client }) => {
@@ -191,5 +289,6 @@ const queryArtificialStreaming = {
191
289
  export const insightsTools = [
192
290
  getAnalytics,
193
291
  getAnalyticsAvailability,
292
+ getAnalyticsRankings,
194
293
  queryArtificialStreaming,
195
294
  ];
@@ -7,11 +7,10 @@ const listReferenceData = {
7
7
  gate: 'read',
8
8
  title: 'List reference data',
9
9
  description: 'Fetch a LabelGrid reference dataset used to resolve the IDs and codes the catalog and release tools expect. Pick ONE dataset with `type`: ' +
10
- '`genres` and `genre_categories` (genre IDs), `languages` (audio/metadata language codes), `contributor_roles`, `instruments`, `distro_outlets` (the outlets/stores available to your account), ' +
11
- '`territories` (country codes), `issue_definitions` (each review issue code’s title, description, severity and whether it blocks distribution; codes are string slugs), ' +
12
- 'or `webhook_event_types` (every webhook event type with its payload schema). ' +
13
- 'Call this when you need a valid ID or code. ' +
14
- 'The same datasets are exposed as MCP resources at labelgrid://reference/{type}; this tool is the fallback for clients that don’t surface resources.',
10
+ '`genres` and `genre_categories` (genre IDs), `languages` (audio/metadata language codes), `contributor_roles`, `instruments`, `distro_outlets` (the outlets/stores available to you), ' +
11
+ '`territories` (country codes), `issue_definitions` (review issue codes — string slugs — with severity and whether they block distribution), ' +
12
+ 'or `webhook_event_types` (event types with their payload schemas). ' +
13
+ 'Also exposed as MCP resources at labelgrid://reference/{type}; this tool is the fallback.',
15
14
  inputShape: {
16
15
  type: z.enum(REFERENCE_TYPES),
17
16
  },
@@ -13,7 +13,7 @@ const releaseId = z.number().int().positive().describe('The release id.');
13
13
  const responseFormat = z
14
14
  .enum(['concise', 'detailed'])
15
15
  .optional()
16
- .describe("'concise' (default) keeps only the high-signal fields (ids always kept); 'detailed' returns the verbatim API response.");
16
+ .describe("'concise' (default) keeps high-signal fields and ids; 'detailed' is the verbatim response.");
17
17
  const getReleaseReview = {
18
18
  name: 'get_release_review',
19
19
  toolset: 'releases',
@@ -10,14 +10,11 @@ const listWebhooks = {
10
10
  toolset: 'webhooks',
11
11
  gate: 'read',
12
12
  title: 'List webhooks',
13
- 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. " +
13
+ description: "Read your webhook subscriptions. `view: 'config'` (the default) lists them — URL, subscribed events, active state — or retrieves one when `webhook_id` is given. " +
14
14
  "`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.",
15
15
  inputShape: {
16
16
  webhook_id: webhookId,
17
- view: z
18
- .enum(['config', 'logs'])
19
- .optional()
20
- .describe('config (default) reads subscriptions; logs reads a webhook’s delivery log.'),
17
+ view: z.enum(['config', 'logs']).optional().describe('config (default) or logs.'),
21
18
  },
22
19
  annotations: { readOnlyHint: true },
23
20
  handler: (args, { client }) => {
@@ -46,11 +43,11 @@ const manageWebhook = {
46
43
  gate: 'safe_write',
47
44
  title: 'Manage a webhook',
48
45
  description: 'Manage a webhook subscription. Pick ONE action with `action`: ' +
49
- '`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. ' +
46
+ '`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. ' +
50
47
  '`update` — supply only the fields to change in `fields`: name, url, events, or is_active (false pauses deliveries). ' +
51
- '`delete` — permanently removes the subscription; it stops receiving events. ' +
52
- '`test` — sends a test event to confirm reachability and signature verification; safe to repeat. ' +
53
- '`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. ' +
48
+ '`delete` — permanently removes the subscription. ' +
49
+ '`test` — sends a test event to confirm reachability and signature verification. ' +
50
+ '`rotate_secret` — returns a new signing secret — WARNING: the old secret stops working immediately; update your endpoint or deliveries fail verification. ' +
54
51
  '`webhook_id` is required for every action except create.',
55
52
  inputShape: {
56
53
  action: z
@@ -60,7 +57,7 @@ const manageWebhook = {
60
57
  fields: z
61
58
  .record(z.string(), z.unknown())
62
59
  .optional()
63
- .describe('The webhook attributes (create: name, url, events; update: any of those plus is_active), forwarded verbatim.'),
60
+ .describe('The webhook attributes, forwarded verbatim.'),
64
61
  },
65
62
  annotations: { destructiveHint: true },
66
63
  handler: (args, { client }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@labelgrid/mcp",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "mcpName": "io.github.labelgrid/labelgrid-mcp",
5
5
  "description": "Official LabelGrid MCP server — connect your AI client to your LabelGrid account",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  "node": ">=20"
28
28
  },
29
29
  "dependencies": {
30
- "@labelgrid/core": "0.2.0",
30
+ "@labelgrid/core": "0.2.1",
31
31
  "@modelcontextprotocol/sdk": "^1.12.0",
32
32
  "zod": "^3.24.0"
33
33
  }
package/server.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.labelgrid/labelgrid-mcp",
4
4
  "description": "Official LabelGrid MCP server — manage your music catalog, releases, analytics and distribution.",
5
- "version": "0.4.0",
5
+ "version": "0.6.0",
6
6
  "websiteUrl": "https://labelgrid.com",
7
7
  "repository": {
8
8
  "url": "https://github.com/labelgrid/labelgrid-mcp",
@@ -12,7 +12,7 @@
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@labelgrid/mcp",
15
- "version": "0.4.0",
15
+ "version": "0.6.0",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },