@labelgrid/mcp 0.6.1 → 0.8.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 +48 -0
- package/README.md +6 -6
- package/dist/coverage.js +4 -1
- package/dist/projection.js +12 -1
- package/dist/tools/catalog.js +35 -5
- package/dist/tools/distribution.d.ts +1 -1
- package/dist/tools/distribution.js +3 -3
- package/dist/tools/releases.d.ts +1 -1
- package/dist/tools/releases.js +5 -17
- package/dist/tools/types.js +46 -16
- package/package.json +2 -2
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,54 @@ 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
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.8.0] - 2026-09-30
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
|
|
14
|
+
- Refresh the desktop bundle's locked production dependencies to patched
|
|
15
|
+
compatible versions. The MCP SDK remains on v1.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Oversized tool responses now return a bounded `RESULT_TOO_LARGE` error instead
|
|
20
|
+
of a serialized data prefix that could exceed the 400,000-character limit.
|
|
21
|
+
Write operations may already have completed; check their state before retrying.
|
|
22
|
+
- Oversized API and handler errors remain valid JSON, preserving bounded
|
|
23
|
+
diagnostic fields and explicitly marking omitted details.
|
|
24
|
+
- An oversized error keeps its `blocking_issues` list when the error fits after
|
|
25
|
+
bulky API details are dropped; otherwise the list may be truncated.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- `get_delivery_queue` now requires `release_id` and returns the public API's
|
|
30
|
+
canonical release delivery status, including aggregate state, historical
|
|
31
|
+
predicates, and one current state per outlet. It no longer reads or interprets
|
|
32
|
+
raw distribution queue rows; the former outlet, status, and pagination inputs
|
|
33
|
+
have been removed.
|
|
34
|
+
- `confirm_review` documents the `blocking_issues_open` error: it lists the
|
|
35
|
+
blocking issues to resolve, or to answer with a review-issue note, before
|
|
36
|
+
retrying.
|
|
37
|
+
- `enable_beatport` no longer describes onboarding as one-time. Declined or
|
|
38
|
+
canceled requests can be submitted again; a pending request or an already
|
|
39
|
+
verified label does not start another one.
|
|
40
|
+
|
|
41
|
+
## [0.7.0] - 2026-08-20
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- `delete_catalog_item` accepts `replace_with` for writers and publishers. The
|
|
46
|
+
replacement receives every credit before the original is deleted. Other
|
|
47
|
+
entity types reject the parameter without sending a delete request.
|
|
48
|
+
- `query_financials` concise responses include `credit_amount`, preserving
|
|
49
|
+
positive, zero and explicit null values from the API.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- Writer and publisher deletion guidance now names all references that can
|
|
54
|
+
block deletion and documents the `REPLACEMENT_UNAVAILABLE` response.
|
|
55
|
+
|
|
8
56
|
## [0.6.1] - 2026-08-05
|
|
9
57
|
|
|
10
58
|
### Changed
|
package/README.md
CHANGED
|
@@ -129,7 +129,7 @@ tool definitions by `npm run gen-docs` — do not edit it by hand._
|
|
|
129
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). |
|
|
130
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. |
|
|
131
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. |
|
|
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
|
|
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 or artists, unless replace_with reassigns those credits. publisher: refused while still referenced by tracks or label default publishers, unless replace_with reassigns those credits. release: only a never-submitted draft can be deleted. track: refused once the release is no longer an editable draft. `replace_with` — writer and publisher only — is the id inheriting those credits; a 422 REPLACEMENT_UNAVAILABLE means nothing was deleted. |
|
|
133
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`. |
|
|
134
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. |
|
|
135
135
|
|
|
@@ -138,7 +138,7 @@ tool definitions by `npm run gen-docs` — do not edit it by hand._
|
|
|
138
138
|
| Tool | Gate | Description |
|
|
139
139
|
| --- | --- | --- |
|
|
140
140
|
| `get_release_review` | read | Read a release's automated quality-check results. Pick ONE view with `view`: `issues` lists the review issues raised against the release — each with a code (see list_reference_data type issue_definitions), severity, and whether it blocks distribution. `quality_report` returns the Preflight QC quality report — customer-facing issues to review before confirming distribution; Preflight QC is an optional add-on — without it the API returns a 403, surfaced verbatim. |
|
|
141
|
-
| `get_delivery_queue` | read |
|
|
141
|
+
| `get_delivery_queue` | read | Read a release's canonical delivery status: its overall state, current and historical delivery predicates, and one current state per outlet. The API owns all queue-history interpretation. A release outside your account is indistinguishable from an unknown release (`RELEASE_NOT_ACCESSIBLE`). |
|
|
142
142
|
| `get_landing_config` | read | Retrieve a release's smart-link landing-page configuration: enabled state, style/mode, custom copy, action list, pre-order links. Change it via manage_release_links (action update_landing_config). |
|
|
143
143
|
| `list_track_licenses` | read | List a track's licenses (e.g. cover/mechanical or sample clearances), paginated. Pass `license_id` to retrieve one license instead. |
|
|
144
144
|
| `run_release_checks` | write | Run an automated check on a release. Pick ONE with `check`: `validate` returns problems that would block distribution (human-readable `errors` + machine-readable `errors_structured`); it changes nothing and is safe to repeat — run it before distributing. `refresh_quality_report` re-runs the Preflight QC checks (read the report with get_release_review view quality_report); an hourly refresh budget may rate-limit frequent calls. Preflight QC is an optional add-on. |
|
|
@@ -177,8 +177,8 @@ tool definitions by `npm run gen-docs` — do not edit it by hand._
|
|
|
177
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. |
|
|
178
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. |
|
|
179
179
|
| `takedown_release` | full-write | Take a release down from ALL outlets/stores — a final action that removes it everywhere it was delivered. Re-distribution is a fresh submission. |
|
|
180
|
-
| `confirm_review` | full-write | Confirm a release Preflight QC placed on hold, moving it into distribution review, after reviewing the quality report and accepting the release as-is. Safe to repeat. |
|
|
181
|
-
| `enable_beatport` | full-write | Request Beatport onboarding for a label.
|
|
180
|
+
| `confirm_review` | full-write | Confirm a release Preflight QC placed on hold, moving it into distribution review, after reviewing the quality report and accepting the release as-is. If blocking issues remain, the blocking_issues_open error includes blocking_issues to resolve or answer with a review-issue note before retrying. Safe to repeat. |
|
|
181
|
+
| `enable_beatport` | full-write | Request Beatport onboarding for a label. Declined or canceled requests can be submitted again; pending requests and verified labels do not start another request. Confirm the label is correct first. |
|
|
182
182
|
|
|
183
183
|
<!-- TOOLS:END -->
|
|
184
184
|
|
|
@@ -238,7 +238,7 @@ Version 0.3.0 is a **breaking release**: the 83 per-endpoint tools were consolid
|
|
|
238
238
|
| `list_artificial_streams` | `query_artificial_streaming` (`view: 'records'`) |
|
|
239
239
|
| `get_artificial_fee_breakdown` | `query_artificial_streaming` (`view: 'fee_breakdown'`, `period: 'YYYY-MM'`) |
|
|
240
240
|
| `get_analytics` | `get_analytics` (unchanged) |
|
|
241
|
-
| `get_delivery_queue` | `get_delivery_queue` (
|
|
241
|
+
| `get_delivery_queue` | `get_delivery_queue` (`release_id` is now required; returns canonical delivery status) |
|
|
242
242
|
| `get_landing_config` | `get_landing_config` (unchanged) |
|
|
243
243
|
| `list_statements` | `query_financials` (`view: 'statements'`) |
|
|
244
244
|
| `get_statement` | `query_financials` (`view: 'statement_detail'`, `invoice_number: …`) |
|
|
@@ -292,7 +292,7 @@ The server has three gates. Each is fail-closed: a tool is only registered — a
|
|
|
292
292
|
- **distribute a release to stores** — a final submission subject to your account's weekly limit,
|
|
293
293
|
- **take a release down from all stores**,
|
|
294
294
|
- confirm a held release into review,
|
|
295
|
-
- request
|
|
295
|
+
- request Beatport onboarding for a label; declined or canceled requests can be submitted again.
|
|
296
296
|
|
|
297
297
|
Leaving `LABELGRID_ENABLE_FULL_WRITES` unset is the safe default: your AI assistant can prepare and validate everything, but the irreversible submission stays a deliberate, opt-in step.
|
|
298
298
|
|
package/dist/coverage.js
CHANGED
|
@@ -58,7 +58,7 @@ export const COVERAGE = {
|
|
|
58
58
|
'GET /stream-radar/flags': 'query_artificial_streaming',
|
|
59
59
|
'GET /stream-radar/flags/{streamRadarFlag}': 'query_artificial_streaming',
|
|
60
60
|
// delivery
|
|
61
|
-
'GET /
|
|
61
|
+
'GET /releases/{releaseId}/delivery-status': 'get_delivery_queue',
|
|
62
62
|
'GET /releases/{release}/landing-config': 'get_landing_config',
|
|
63
63
|
// finance
|
|
64
64
|
'GET /statements': 'query_financials',
|
|
@@ -141,15 +141,18 @@ export const EXCLUDED = {
|
|
|
141
141
|
'GET /analytics/streams-by-age': 'served by get_analytics (summary)',
|
|
142
142
|
'GET /analytics/shares-by-country': 'served by get_analytics (summary)',
|
|
143
143
|
// Alternate/adjacent surfaces intentionally not exposed in v1.
|
|
144
|
+
'GET /queues/distro': 'raw queue history — use the canonical release delivery status projection instead',
|
|
144
145
|
'POST /releases/{release}/withdraw-review': 'withdraw-review flow — not exposed in v1',
|
|
145
146
|
'GET /resolve/label/{labelSlug}': 'label-website resolution — not exposed in v1',
|
|
146
147
|
'GET /site-settings/{label}': 'label-website settings — not exposed in v1',
|
|
147
148
|
'GET /site-settings/links/{label}': 'label-website settings — not exposed in v1',
|
|
149
|
+
'GET /tracks/{track}/file-upload-attempts/{uploadAttempt}': 'per-upload audio processing status — not exposed in v1',
|
|
148
150
|
'GET /tracks/{track}/licenses/{trackLicense}/download': 'license file download — not exposed in v1',
|
|
149
151
|
'GET /transactions/csv': 'transaction CSV export — not exposed in v1',
|
|
150
152
|
};
|
|
151
153
|
export const PENDING_DOCS = {
|
|
152
154
|
'GET /account': 'get_account',
|
|
153
155
|
'GET /analytics/availability': 'get_analytics_availability',
|
|
156
|
+
'GET /releases/{releaseId}/delivery-status': 'get_delivery_queue',
|
|
154
157
|
'GET /tracks/{track}/files/{assetType}/download-url': 'get_asset',
|
|
155
158
|
};
|
package/dist/projection.js
CHANGED
|
@@ -55,7 +55,17 @@ export const CONCISE_ALLOWLISTS = {
|
|
|
55
55
|
'message',
|
|
56
56
|
'created_at',
|
|
57
57
|
],
|
|
58
|
-
get_delivery_queue: [
|
|
58
|
+
get_delivery_queue: [
|
|
59
|
+
'state',
|
|
60
|
+
'currently_live',
|
|
61
|
+
'ever_submitted',
|
|
62
|
+
'ever_delivered',
|
|
63
|
+
'outlet',
|
|
64
|
+
'operation',
|
|
65
|
+
'queue_id',
|
|
66
|
+
'updated_at',
|
|
67
|
+
'error_code',
|
|
68
|
+
],
|
|
59
69
|
query_artificial_streaming: [
|
|
60
70
|
'dsp',
|
|
61
71
|
'country',
|
|
@@ -72,6 +82,7 @@ export const CONCISE_ALLOWLISTS = {
|
|
|
72
82
|
'status',
|
|
73
83
|
'currency',
|
|
74
84
|
'gross_usd',
|
|
85
|
+
'credit_amount',
|
|
75
86
|
'labelgrid_fee',
|
|
76
87
|
'platform_fee_usd',
|
|
77
88
|
'ugc_fee_usd',
|
package/dist/tools/catalog.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* `response_format: 'detailed'` returning the verbatim API response.
|
|
12
12
|
*/
|
|
13
13
|
import { statSync } from 'node:fs';
|
|
14
|
-
import { ENTITIES, ENTITY_NAMES, assertAllowedExtension, } from '@labelgrid/core';
|
|
14
|
+
import { ENTITIES, ENTITY_NAMES, REPLACEMENT_ENTITIES, assertAllowedExtension, } from '@labelgrid/core';
|
|
15
15
|
import { z } from 'zod';
|
|
16
16
|
import { applyProjection } from '../projection.js';
|
|
17
17
|
/** Accepted image extensions for the catalog image uploads. */
|
|
@@ -135,17 +135,47 @@ const updateCatalogItem = {
|
|
|
135
135
|
return client.patch(`${spec.path}/${args.id}`, args.fields);
|
|
136
136
|
},
|
|
137
137
|
};
|
|
138
|
+
/** 'writer and publisher' — read off the registry, never spelled out by hand. */
|
|
139
|
+
const REPLACEMENT_ENTITY_LIST = REPLACEMENT_ENTITIES.join(' and ');
|
|
138
140
|
const deleteCatalogItem = {
|
|
139
141
|
name: 'delete_catalog_item',
|
|
140
142
|
toolset: 'catalog',
|
|
141
143
|
gate: 'safe_write',
|
|
142
144
|
title: 'Delete a catalog item',
|
|
143
|
-
description: `Delete a catalog entity. The API refuses deletes that would orphan data — ${entityDoc((s) => s.deleteNote)}
|
|
144
|
-
|
|
145
|
+
description: `Delete a catalog entity. The API refuses deletes that would orphan data — ${entityDoc((s) => s.deleteNote)}` +
|
|
146
|
+
` \`replace_with\` — ${REPLACEMENT_ENTITY_LIST} only — is the id inheriting those credits; a 422 REPLACEMENT_UNAVAILABLE means nothing was deleted.`,
|
|
147
|
+
inputShape: {
|
|
148
|
+
entity: entityArg,
|
|
149
|
+
id: idArg,
|
|
150
|
+
replace_with: z
|
|
151
|
+
.number()
|
|
152
|
+
.int()
|
|
153
|
+
.positive()
|
|
154
|
+
.optional()
|
|
155
|
+
.describe(`${REPLACEMENT_ENTITY_LIST} only: the id that takes over every credit held by the one being deleted.`),
|
|
156
|
+
},
|
|
145
157
|
annotations: { destructiveHint: true },
|
|
146
158
|
handler: (args, { client }) => {
|
|
147
|
-
const
|
|
148
|
-
|
|
159
|
+
const entity = args.entity;
|
|
160
|
+
const spec = ENTITIES[entity];
|
|
161
|
+
const path = `${spec.path}/${args.id}`;
|
|
162
|
+
const replaceWith = args.replace_with;
|
|
163
|
+
if (replaceWith === undefined) {
|
|
164
|
+
return client.delete(path);
|
|
165
|
+
}
|
|
166
|
+
// Only two endpoints take the parameter. Dropping it for the others would
|
|
167
|
+
// delete on the caller's behalf while silently ignoring the reassignment
|
|
168
|
+
// they asked for, so the call is refused before anything is sent.
|
|
169
|
+
if (!spec.acceptsDeleteReplacement) {
|
|
170
|
+
return Promise.resolve({
|
|
171
|
+
error: {
|
|
172
|
+
code: 'INVALID_SELECTOR',
|
|
173
|
+
message: `The ${entity} delete does not accept replace_with — only ${REPLACEMENT_ENTITY_LIST} deletes do. Delete without it, or clear the references first.`,
|
|
174
|
+
status: 0,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return client.delete(path, { replace_with: replaceWith });
|
|
149
179
|
},
|
|
150
180
|
};
|
|
151
181
|
/** Maps the label image targets to the API's imageType path segment. */
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* These wrap: finalized audio/artwork/motion-artwork uploads (via the
|
|
8
8
|
* presigned-URL flow or multipart), license file management, the FINAL
|
|
9
9
|
* distribute/takedown actions, the Preflight-QC confirm-review step, and
|
|
10
|
-
*
|
|
10
|
+
* Beatport onboarding.
|
|
11
11
|
*/
|
|
12
12
|
import type { ToolDef } from './types.js';
|
|
13
13
|
export declare const distributionTools: ToolDef[];
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* These wrap: finalized audio/artwork/motion-artwork uploads (via the
|
|
8
8
|
* presigned-URL flow or multipart), license file management, the FINAL
|
|
9
9
|
* distribute/takedown actions, the Preflight-QC confirm-review step, and
|
|
10
|
-
*
|
|
10
|
+
* Beatport onboarding.
|
|
11
11
|
*/
|
|
12
12
|
import { assertAllowedExtension, uploadViaPresignedUrl } from '@labelgrid/core';
|
|
13
13
|
import { z } from 'zod';
|
|
@@ -239,7 +239,7 @@ const confirmReview = {
|
|
|
239
239
|
toolset: 'distribution',
|
|
240
240
|
gate: 'full_write',
|
|
241
241
|
title: 'Confirm a held release into review',
|
|
242
|
-
description: 'Confirm a release Preflight QC placed on hold, moving it into distribution review, after reviewing the quality report and accepting the release as-is. Safe to repeat.',
|
|
242
|
+
description: 'Confirm a release Preflight QC placed on hold, moving it into distribution review, after reviewing the quality report and accepting the release as-is. If blocking issues remain, the blocking_issues_open error includes blocking_issues to resolve or answer with a review-issue note before retrying. Safe to repeat.',
|
|
243
243
|
inputShape: { release_id: releaseId },
|
|
244
244
|
annotations: { destructiveHint: true, idempotentHint: true },
|
|
245
245
|
handler: (args, { client }) => client.post(`/releases/${args.release_id}/confirm-review`),
|
|
@@ -249,7 +249,7 @@ const enableBeatport = {
|
|
|
249
249
|
toolset: 'distribution',
|
|
250
250
|
gate: 'full_write',
|
|
251
251
|
title: 'Request Beatport onboarding for a label',
|
|
252
|
-
description: 'Request Beatport onboarding for a label.
|
|
252
|
+
description: 'Request Beatport onboarding for a label. Declined or canceled requests can be submitted again; pending requests and verified labels do not start another request. Confirm the label is correct first.',
|
|
253
253
|
inputShape: { label_id: z.number().int().positive().describe('The label id.') },
|
|
254
254
|
annotations: { destructiveHint: true },
|
|
255
255
|
handler: (args, { client }) => client.post(`/labels/${args.label_id}/enable-beatport`),
|
package/dist/tools/releases.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Releases toolset: release-level reads (review results,
|
|
2
|
+
* Releases toolset: release-level reads (review results, delivery status,
|
|
3
3
|
* the smart-link landing config, track licenses) plus the safe-write release
|
|
4
4
|
* checks, landing-page management and review-issue notes.
|
|
5
5
|
*
|
package/dist/tools/releases.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Releases toolset: release-level reads (review results,
|
|
2
|
+
* Releases toolset: release-level reads (review results, delivery status,
|
|
3
3
|
* the smart-link landing config, track licenses) plus the safe-write release
|
|
4
4
|
* checks, landing-page management and review-issue notes.
|
|
5
5
|
*
|
|
@@ -39,27 +39,15 @@ const getDeliveryQueue = {
|
|
|
39
39
|
name: 'get_delivery_queue',
|
|
40
40
|
toolset: 'releases',
|
|
41
41
|
gate: 'read',
|
|
42
|
-
title: 'Get
|
|
43
|
-
description: "
|
|
42
|
+
title: 'Get release delivery status',
|
|
43
|
+
description: "Read a release's canonical delivery status: its overall state, current and historical delivery predicates, and one current state per outlet. The API owns all queue-history interpretation. A release outside your account is indistinguishable from an unknown release (`RELEASE_NOT_ACCESSIBLE`).",
|
|
44
44
|
inputShape: {
|
|
45
|
-
release_id:
|
|
46
|
-
outlet_id: z.number().int().positive().optional().describe('Filter to one outlet/store.'),
|
|
47
|
-
status: z.string().optional().describe('Filter by delivery status.'),
|
|
48
|
-
page: z.number().int().positive().optional(),
|
|
49
|
-
per_page: z.number().int().positive().optional(),
|
|
45
|
+
release_id: releaseId,
|
|
50
46
|
response_format: responseFormat,
|
|
51
47
|
},
|
|
52
48
|
annotations: { readOnlyHint: true },
|
|
53
49
|
handler: async (args, { client }) => {
|
|
54
|
-
const result = await client.get(
|
|
55
|
-
page: args.page,
|
|
56
|
-
per_page: args.per_page,
|
|
57
|
-
filter: {
|
|
58
|
-
release_id: args.release_id,
|
|
59
|
-
outlet_id: args.outlet_id,
|
|
60
|
-
status: args.status,
|
|
61
|
-
},
|
|
62
|
-
});
|
|
50
|
+
const result = await client.get(`/releases/${args.release_id}/delivery-status`);
|
|
63
51
|
return applyProjection(result, 'get_delivery_queue', args.response_format);
|
|
64
52
|
},
|
|
65
53
|
};
|
package/dist/tools/types.js
CHANGED
|
@@ -14,14 +14,22 @@ export function toToolResult(r) {
|
|
|
14
14
|
if ('data' in r) {
|
|
15
15
|
const text = JSON.stringify(r.data ?? null, null, 2);
|
|
16
16
|
if (text.length > MAX_TOOL_TEXT) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: JSON.stringify({
|
|
22
|
+
error: {
|
|
23
|
+
code: 'RESULT_TOO_LARGE',
|
|
24
|
+
message: 'Response exceeded the 400,000-character limit; response data was omitted.',
|
|
25
|
+
status: 0,
|
|
26
|
+
suggestion: 'Use pagination or filters to narrow reads. If this was a write, it may already have completed. Check its state before retrying.',
|
|
27
|
+
},
|
|
28
|
+
}, null, 2),
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
isError: true,
|
|
32
|
+
};
|
|
25
33
|
}
|
|
26
34
|
return { content: [{ type: 'text', text }] };
|
|
27
35
|
}
|
|
@@ -29,24 +37,46 @@ export function toToolResult(r) {
|
|
|
29
37
|
if (errorText.length <= MAX_TOOL_TEXT) {
|
|
30
38
|
return { content: [{ type: 'text', text: errorText }], isError: true };
|
|
31
39
|
}
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
40
|
+
// Omit bulky API details first, preserving the diagnostic core when it fits.
|
|
41
|
+
// Blocking issues are what a caller needs to recover, so they are kept
|
|
42
|
+
// unless they are too large on their own.
|
|
35
43
|
const bounded = { ...r.error };
|
|
36
44
|
if (bounded.errors !== undefined)
|
|
37
45
|
bounded.errors = '[truncated]';
|
|
38
46
|
if (bounded.errors_structured !== undefined)
|
|
39
47
|
bounded.errors_structured = '[truncated]';
|
|
48
|
+
if (bounded.details !== undefined)
|
|
49
|
+
bounded.details = '[truncated]';
|
|
40
50
|
const boundedText = JSON.stringify({ error: bounded }, null, 2);
|
|
41
51
|
if (boundedText.length <= MAX_TOOL_TEXT) {
|
|
42
52
|
return { content: [{ type: 'text', text: boundedText }], isError: true };
|
|
43
53
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
if (bounded.blocking_issues !== undefined) {
|
|
55
|
+
bounded.blocking_issues = '[truncated]';
|
|
56
|
+
const withoutIssuesText = JSON.stringify({ error: bounded }, null, 2);
|
|
57
|
+
if (withoutIssuesText.length <= MAX_TOOL_TEXT) {
|
|
58
|
+
return { content: [{ type: 'text', text: withoutIssuesText }], isError: true };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Bound raw fields before encoding. Four string prefixes of at most 8,000 code units
|
|
62
|
+
// leave room even when JSON escaping expands each code unit to six characters.
|
|
63
|
+
const compact = {
|
|
64
|
+
code: boundDiagnostic(bounded.code),
|
|
65
|
+
message: boundDiagnostic(bounded.message),
|
|
66
|
+
status: bounded.status,
|
|
67
|
+
field: bounded.field === undefined ? undefined : boundDiagnostic(bounded.field),
|
|
68
|
+
suggestion: bounded.suggestion === undefined ? undefined : boundDiagnostic(bounded.suggestion),
|
|
69
|
+
retry_after_seconds: bounded.retry_after_seconds,
|
|
70
|
+
errors: bounded.errors,
|
|
71
|
+
errors_structured: bounded.errors_structured,
|
|
72
|
+
details: bounded.details,
|
|
73
|
+
blocking_issues: bounded.blocking_issues,
|
|
74
|
+
};
|
|
48
75
|
return {
|
|
49
|
-
content: [{ type: 'text', text:
|
|
76
|
+
content: [{ type: 'text', text: JSON.stringify({ error: compact }, null, 2) }],
|
|
50
77
|
isError: true,
|
|
51
78
|
};
|
|
52
79
|
}
|
|
80
|
+
function boundDiagnostic(value) {
|
|
81
|
+
return value.length > 8_000 ? `${value.slice(0, 8_000)} [truncated]` : value;
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@labelgrid/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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.
|
|
30
|
+
"@labelgrid/core": "0.2.3",
|
|
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.
|
|
5
|
+
"version": "0.8.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.
|
|
15
|
+
"version": "0.8.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|