UNPKG

@withpica/mcp-server

Version:

MCP Server for withPICA — enables AI assistants to interact with your withPICA music catalog

164 lines • 9.22 kB
// Copyright (c) 2025-2026 Withpica Ltd. All rights reserved. import { formatStructuredList, formatSuccess } from "@withpica/mcp-utils"; export class LabelsTools { pica; constructor(pica) { this.pica = pica; } getTools() { return [ { definition: { name: "pica_labels_query", scope: "read:catalog", audiences: ["stdio", "http", "public"], tier: "read", description: "Find label and distributor organisations by name. Returns organisation IDs " + "suitable for releases.label_organization_id / distributor_organization_id " + "(via pica_releases_create or pica_releases_update). " + "Labels and distributors in PICA are organisations with org_type='label' or " + "org_type='distributor' — only parties already known to PICA appear here. " + "→ then: pica_releases_create / pica_releases_update (set label_organization_id / distributor_organization_id)", workflows: "infrastructure", vernacular_kind: "specialist", vernacular_reason: "Specialist context; not yet covered by the Creator Question Atlas — pending telemetry-driven addition per ADR-226 Phase 4 substrate writes.", inputSchema: { type: "object", properties: { query: { type: "string", description: "Partial label name (substring match, case-insensitive). Omit to list all labels.", }, limit: { type: "number", description: "Max results (default: 50, max: 100)", }, org_type: { type: "string", enum: ["label", "distributor"], description: "Narrow to one party type. Omit to search both labels and distributors.", }, }, }, annotations: { readOnlyHint: true, }, }, executor: this.queryLabels.bind(this), }, { definition: { name: "pica_labels_create", scope: "write:catalog", audiences: ["stdio", "http", "public"], tier: "write", description: "Create a label or distributor organisation when pica_labels_query " + "finds no match. Creates an unclaimed organisation row " + "(verification pending) whose ID can be passed to " + "pica_releases_create / pica_releases_update as " + "label_organization_id or distributor_organization_id. " + "Dedup is server-side: external identifiers (when supplied) resolve " + "the label's global identity and an organisation already bridged to " + "it is returned even if the name differs; otherwise an existing " + "case-insensitive name match is returned instead of creating a duplicate. " + "→ first: pica_labels_query (covers labels and distributors; the server also dedups across both types) " + "→ then: pica_releases_update (link the returned id)", workflows: "infrastructure", vernacular_kind: "specialist", vernacular_reason: "Specialist context; not yet covered by the Creator Question Atlas — pending telemetry-driven addition per ADR-226 Phase 4 substrate writes.", inputSchema: { type: "object", properties: { name: { type: "string", description: "Label or distributor name (e.g. 'Wings Music Group')", }, org_type: { type: "string", enum: ["label", "distributor"], description: "'label' for the releasing label, 'distributor' for the distribution company. An organisation wearing both hats is one row — link the same id into both release fields.", }, country: { type: "string", description: "ISO country code (e.g. 'GB'), if known", }, website: { type: "string", description: "Official website URL, if known", }, contact_email: { type: "string", description: "Public contact email, if known — used later to invite the organisation to claim itself", }, identifiers: { type: "object", description: "External label identifiers, if known. Supplying any " + "of these makes dedup identifier-based instead of name-based: " + "two creates resolving to the same global label identity return " + "the same organisation, whatever the names say.", properties: { musicbrainz_label_id: { type: "string", description: "MusicBrainz label MBID", }, isni: { type: "string", description: "ISNI of the label", }, label_code: { type: "string", description: "GVL/IFPI label code (LC number, digits only)", }, discogs_label_id: { type: "string", description: "Discogs label id", }, wikidata_id: { type: "string", description: "Wikidata entity id (e.g. 'Q38903')", }, deezer_label_id: { type: "string", description: "Deezer label id", }, }, }, }, required: ["name", "org_type"], }, }, executor: this.createLabel.bind(this), }, ]; } async createLabel(args) { const label = await this.pica.labels.create({ name: args.name, org_type: args.org_type, ...(args.country ? { country: args.country } : {}), ...(args.website ? { website: args.website } : {}), ...(args.contact_email ? { contact_email: args.contact_email } : {}), ...(args.identifiers && typeof args.identifiers === "object" ? { identifiers: args.identifiers } : {}), }); if (!label) { return formatSuccess("Label create completed but the server returned no organisation data — query pica_labels_query to confirm state before retrying.", { label: null }); } if (label.already_existed) { return formatSuccess(`"${label.name}" already exists as a ${label.org_type ?? "party"} organisation (id ${label.id}) — link it via pica_releases_update instead of creating a duplicate.`, label); } return formatSuccess(`Created ${args.org_type} organisation "${label.name}" (id ${label.id}, verification pending). Link it to the release via pica_releases_update (label_organization_id / distributor_organization_id).`, label); } async queryLabels(args) { const labels = await this.pica.labels.list({ query: args.query, limit: args.limit, org_type: args.org_type, }); return formatStructuredList(labels, "label", { total: labels.length }); } } //# sourceMappingURL=labels.js.map