UNPKG

@withpica/mcp-server

Version:

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

144 lines • 7.93 kB
// Copyright (c) 2025-2026 Withpica Ltd. All rights reserved. import { formatStructuredList, formatSuccess } from "@withpica/mcp-utils"; export class PartyClaimsTools { pica; constructor(pica) { this.pica = pica; } getTools() { return [ { definition: { name: "pica_party_claim_invite", scope: "write:catalog", audiences: ["stdio", "http", "public"], tier: "write", description: "Invite a label, distributor, or publisher to claim its unclaimed " + "organisation on PICA. The user supplies the recipient email — that " + "address receives a single-use claim link, and on redemption the " + "claimant becomes the owner of the organisation and an active " + "org-to-org connection is created with this catalog (never " + "membership). Only unclaimed party organisations (created via " + "pica_labels_create, verification pending, no members) can be " + "invited. If another claim is already open on the organisation, " + "both claims freeze as disputed for manual review. " + "→ first: pica_labels_query (find the organisation id) " + "→ then: pica_party_claim_status (track redemption)", 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: { target_organisation_id: { type: "string", description: "id of the unclaimed party organisation to invite (from pica_labels_query / pica_labels_create)", }, invited_email: { type: "string", description: "Recipient email supplied by the user — the consent and identity anchor. The claim link goes to exactly this address and only an account with this email can redeem it.", }, release_id: { type: "string", description: "Optional release (in this catalog) that names the party — gives the invite email its context", }, recording_id: { type: "string", description: "Optional recording (in this catalog) that names the party — alternative context to release_id", }, }, required: ["target_organisation_id", "invited_email"], }, }, executor: this.sendInvite.bind(this), }, { definition: { name: "pica_party_claim_status", scope: "read:catalog", audiences: ["stdio", "http", "public"], tier: "read", description: "List the party claim invites this organisation has sent and their " + "lifecycle status (pending / claimed / disputed / expired / revoked " + "/ under_review / approved / rejected). A 'claimed' row means the " + "party completed the claim: their organisation is verified and an " + "active org-to-org connection exists. " + "→ first: pica_party_claim_invite (send a claim invite)", 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: { target_organisation_id: { type: "string", description: "Narrow to claims against one party organisation id", }, status: { type: "string", enum: [ "pending", "under_review", "approved", "claimed", "rejected", "disputed", "expired", "revoked", ], description: "Narrow to one lifecycle status", }, limit: { type: "number", minimum: 1, maximum: 100, description: "Max results (default: 50, max: 100)", }, offset: { type: "number", minimum: 0, description: "Skip this many claims (default 0). Page while has_more is true.", }, }, }, annotations: { readOnlyHint: true, }, }, executor: this.claimStatus.bind(this), }, ]; } async sendInvite(args) { const result = await this.pica.partyClaims.invite({ target_organisation_id: args.target_organisation_id, invited_email: args.invited_email, ...(args.release_id ? { release_id: args.release_id } : {}), ...(args.recording_id ? { recording_id: args.recording_id } : {}), }); if (!result || !result.invite) { return formatSuccess("Claim invite call completed but the server returned no invite data — call pica_party_claim_status to confirm state before retrying.", { invite: null }); } if (result.disputed) { return formatSuccess("Another claim is already open on this organisation — BOTH claims are now frozen as disputed and no email was sent. The PICA team will adjudicate; track the outcome via pica_party_claim_status.", result); } return formatSuccess(`Claim invite sent to ${result.invite.invited_email}. When they redeem it they become the owner of the claimed organisation and an active org-to-org connection is created with this catalog. Track it via pica_party_claim_status.`, result); } async claimStatus(args) { // listPage carries the route's real total (list() returned only the // page, so "total" was the page length — row-cap T3). const page = await this.pica.partyClaims.listPage({ target_organisation_id: args.target_organisation_id, status: args.status, limit: args.limit, offset: args.offset, }); return formatStructuredList(page.data, "party claim invite", { total: page.total, has_more: page.hasMore, }); } } //# sourceMappingURL=party-claims.js.map