UNPKG

@withpica/mcp-server

Version:

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

341 lines • 18.6 kB
// Copyright (c) 2025-2026 Withpica Ltd. All rights reserved. import { formatAsText, formatList, formatSuccess } from "@withpica/mcp-utils"; import { notifyParties, summarizeNotifications, withBillingGate, } from "@withpica/mcp-utils"; import { AGREEMENT_TYPES_WRITE_PROPERTIES, AGREEMENT_TYPES_CREATE_REQUIRED, AGREEMENT_TYPES_UPDATE_REQUIRED, } from "@withpica/mcp-utils"; export class AgreementTypesTools { pica; constructor(pica) { this.pica = pica; } getResource(type) { switch (type) { case "template": return this.pica.agreementTemplates; case "producer": return this.pica.producerAgreements; case "work_for_hire": return this.pica.workForHire; default: throw new Error(`Unknown agreement type: ${type}`); } } labelFor(type) { switch (type) { case "template": return "Agreement template"; case "producer": return "Producer agreement"; case "work_for_hire": return "Work-for-hire agreement"; } } getTools() { return [ // ── Unified CRUD ──────────────────────────────────────────────── { definition: { name: "pica_agreement_types_query", scope: "read:agreements", audiences: ["stdio", "http", "public"], tier: "read", description: "List agreements by type — templates, producer agreements, or work-for-hire contracts. " + "For one agreement in full, use pica_agreement_types_inspect; " + "this one lists by type.", 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: { type: { type: "string", enum: ["template", "producer", "work_for_hire"], description: "Agreement sub-type to query", }, limit: { type: "number", description: "Max results", }, }, required: ["type"], }, }, executor: this.query.bind(this), }, { definition: { name: "pica_agreement_types_inspect", scope: "read:agreements", audiences: ["stdio", "http", "public"], tier: "read", description: "Get a specific agreement by type and ID — full details including body, terms, and metadata. " + "Producer and work-for-hire agreements are money: without the org's finance access the body, fee, advance and points are withheld and the row carries finance_access_required: true. " + "To browse every agreement of a type, use " + "pica_agreement_types_query; this one needs the 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: { type: { type: "string", enum: ["template", "producer", "work_for_hire"], description: "Agreement sub-type", }, id: { type: "string", description: "Agreement ID", }, }, required: ["type", "id"], }, }, executor: this.inspect.bind(this), }, { definition: { name: "pica_agreement_types_create", scope: "write:agreements", audiences: ["stdio", "http", "public"], tier: "write", description: "Create a new agreement of the specified type. Three contract shapes: " + "split sheet — informal, all collaborators agree to publishing splits pre-registration (prefer pica_split_sheet_generate for that flow); " + "work-for-hire — contractor waives royalty share, takes a flat fee + services description; " + "producer agreement — producer takes points off the top of master royalties + deliverables. " + "Pick the shape that matches the deal. Three branches dispatched via 'type': " + "template (needs name + category + templateText), " + "producer (needs producer_id + royalty_points + deliverables + work_id|recording_id), " + "work_for_hire (needs contractor_id + contractor_role + fee_amount + services_description + work_id|recording_id). " + "Creating a producer or work-for-hire agreement needs the org's finance access (refused with FINANCE_ACCESS_REQUIRED otherwise). " + "Prefer pica_agreement_templates_duplicate when the user wants a copy of an existing template.", 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: AGREEMENT_TYPES_WRITE_PROPERTIES, required: [...AGREEMENT_TYPES_CREATE_REQUIRED], }, }, executor: withBillingGate(this.pica, "agreement type creation", this.create.bind(this)), }, { definition: { name: "pica_agreement_types_update", scope: "write:agreements", audiences: ["stdio", "http", "public"], tier: "write", description: "Update an existing agreement by type and ID. " + "Note: PATCH on producer_agreements and work_for_hire_agreements is action-only " + "(send_for_signature / countersign / mark_advance_paid / mark_paid / record_recoupment / " + "calculate_royalty), not generic field-level; every action except send_for_signature needs the org's finance access. Template branch accepts field updates. " + "Prefer pica_agreement_templates_set_default when the change is " + "just making a template the default for its type.", 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: AGREEMENT_TYPES_WRITE_PROPERTIES, required: [...AGREEMENT_TYPES_UPDATE_REQUIRED], }, }, executor: withBillingGate(this.pica, "agreement type update", this.update.bind(this)), }, { definition: { name: "pica_agreement_types_delete", scope: "write:agreements", audiences: ["stdio", "http", "public"], tier: "destructive", description: "Delete a template / producer agreement / work-for-hire record by type + id — the " + "agreement_types family. Standard agreements (master_recording, publishing_admin, etc.) " + "use pica_agreements_delete instead. No undo — use " + "pica_agreement_types_inspect to check the record first.", workflows: "infrastructure", vernacular_kind: "specialist", vernacular_reason: "Destructive operation; invoked by exact name after explicit user confirmation in a specialist flow.", inputSchema: { type: "object", properties: { type: { type: "string", enum: ["template", "producer", "work_for_hire"], description: "Agreement sub-type", }, id: { type: "string", description: "Agreement UUID from pica_agreement_types_query.", }, }, required: ["type", "id"], }, }, executor: withBillingGate(this.pica, "agreement type deletion", this.delete.bind(this)), }, // ── Template-specific operations ──────────────────────────────── { definition: { name: "pica_agreement_templates_render", scope: "read:agreements", audiences: ["stdio", "http", "public"], tier: "read", description: "Fill an agreement template with work and people data — generates a ready-to-send agreement. Pass the template ID plus work_id and/or people_ids to populate the placeholders with real catalog data. " + "Find the template id with pica_agreement_types_query type:template; " + "this one only fills it in and returns the text.", workflows: ["agreement-required"], 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: { id: { type: "string", description: "Template ID to render", }, work_id: { type: "string", description: "Work ID to pull title, credits, and metadata from", }, people_ids: { type: "array", items: { type: "string" }, description: "People IDs to include as parties in the agreement", }, variables: { type: "object", description: "Additional variables to substitute — keys match placeholder names in the template body", }, }, required: ["id"], }, }, executor: this.renderTemplate.bind(this), }, { definition: { name: "pica_agreement_templates_duplicate", scope: "write:agreements", audiences: ["stdio", "http", "public"], tier: "write", description: "Duplicate an agreement template — creates a copy you can customise without changing the original. " + "Prefer pica_agreement_types_update to edit the original in place.", 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: { id: { type: "string", description: "Template ID to duplicate", }, }, required: ["id"], }, }, executor: withBillingGate(this.pica, "template duplication", this.duplicateTemplate.bind(this)), }, { definition: { name: "pica_agreement_templates_set_default", scope: "write:agreements", audiences: ["stdio", "http", "public"], tier: "write", description: "Set a template as the default for its type — when creating agreements of that type, this template will be suggested first. " + "Use pica_agreement_types_query type:template to list the " + "templates you can choose from.", 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: { id: { type: "string", description: "Template ID to set as default", }, }, required: ["id"], }, }, executor: withBillingGate(this.pica, "template default", this.setDefaultTemplate.bind(this)), }, ]; } // ── Unified CRUD executors ────────────────────────────────────────── async query(args) { const resource = this.getResource(args.type); const items = await resource.list(); return formatList(items, { total: items.length }); } async inspect(args) { const resource = this.getResource(args.type); const item = await resource.get(args.id); return formatAsText(item); } async create(args) { const { type, ...fields } = args; const resource = this.getResource(type); const item = await resource.create(fields); // Notification for producer agreements if (type === "producer" && fields.producer_id) { const results = await notifyParties(this.pica, [ { person_id: fields.producer_id, message: `a producer agreement has been created for "${fields.title || "untitled"}" — please review the terms`, work_id: fields.work_id, notification_type: "agreement_created", }, ]); const notifySummary = summarizeNotifications(results); if (notifySummary) { return formatSuccess(`Producer agreement created. ${notifySummary}`, item); } } // Notification for work-for-hire if (type === "work_for_hire" && fields.contractor_id) { const results = await notifyParties(this.pica, [ { person_id: fields.contractor_id, message: `a work-for-hire agreement has been created: "${fields.title || "untitled"}" — please review`, work_id: fields.work_id, notification_type: "agreement_created", }, ]); const notifySummary = summarizeNotifications(results); if (notifySummary) { return formatSuccess(`Work-for-hire agreement created. ${notifySummary}`, item); } } return formatSuccess(`${this.labelFor(type)} created`, item); } async update(args) { const { type, id, ...updates } = args; const resource = this.getResource(type); const item = await resource.update(id, updates); return formatSuccess(`${this.labelFor(type)} updated`, item); } async delete(args) { const { type, id } = args; const resource = this.getResource(type); await resource.delete(id); return formatSuccess(`${this.labelFor(type)} ${id} deleted`); } // ── Template-specific executors ───────────────────────────────────── async renderTemplate(args) { const { id, ...renderData } = args; const rendered = await this.pica.agreementTemplates.render(id, renderData); return formatSuccess("Template rendered with catalog data", rendered); } async duplicateTemplate(args) { const duplicate = await this.pica.agreementTemplates.duplicate(args.id); return formatSuccess("Template duplicated", duplicate); } async setDefaultTemplate(args) { const result = await this.pica.agreementTemplates.setDefault(args.id); return formatSuccess("Template set as default for its type", result); } } //# sourceMappingURL=agreement-types.js.map