UNPKG

@withpica/mcp-server

Version:

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

106 lines • 4.78 kB
// Copyright (c) 2025-2026 Withpica Ltd. All rights reserved. /** * The scope of a portfolio relationship, as the assistant tools accept it * (ADR-333 D3). The main MCP server cannot import the app, so this mirrors * `relationshipScopeSchema` in lib/services/portfolio-relationship-model.ts * with snake_case names. lib/services/__tests__/relationship-scope-mcp- * parity.test.ts fails if a field, an allowed value or a limit drifts. * * Every field is optional: the app fills the defaults (whole catalogue, both * halves, worldwide, no end date, money on only for an administrator, * directory off). The rules that join two fields (for example "worldwide * cannot be combined with other territories") are enforced by the app, which * refuses with a plain reason; the descriptions below state them. * * Deliberately has no imports, so the parity test can load it directly. */ export const SCOPE_HALVES = ["both", "composition", "master"]; export const SCOPE_CATALOGUES = ["whole", "chosen"]; export const SCOPE_MAX_TERRITORIES = 300; export const SCOPE_MAX_ITEMS = 5000; export const SCOPE_TERRITORY_PATTERN = "^[A-Z]{2}$"; export const SCOPE_DATE_PATTERN = "^\\d{4}-\\d{2}-\\d{2}$"; /** The tool argument name for each field of the app's scope. */ export const SCOPE_FIELD_NAMES = { half: "half", territories: "territories", term_starts_on: "termStartsOn", term_ends_on: "termEndsOn", money: "money", directory: "directory", catalogue: "catalogue", work_ids: "workIds", recording_ids: "recordingIds", }; /** JSON Schema for the optional `scope` argument of grant, invite and answer. */ export const RELATIONSHIP_SCOPE_INPUT_SCHEMA = { type: "object", description: "What the relationship covers. Leave out any field to use the default: the whole catalogue, " + "both halves, worldwide, until revoked, statements hidden (shown by default only to an " + "administrator), directory off.", additionalProperties: false, properties: { half: { type: "string", enum: [...SCOPE_HALVES], description: "Which half of the songs: both, composition (the song and its publishing) or master (the recordings).", }, territories: { type: "array", items: { type: "string", pattern: SCOPE_TERRITORY_PATTERN }, minItems: 1, maxItems: SCOPE_MAX_TERRITORIES, description: 'Countries covered. Only ["WW"] (worldwide) is accepted today: limiting a relationship to some countries is not available yet, and any other value is refused.', }, term_starts_on: { type: ["string", "null"], pattern: SCOPE_DATE_PATTERN, description: "First day covered, YYYY-MM-DD (UTC). Leave out or null to start when it is accepted.", }, term_ends_on: { type: ["string", "null"], pattern: SCOPE_DATE_PATTERN, description: "Last day covered, YYYY-MM-DD (UTC), inclusive. Leave out or null for until revoked. On that date access ends by itself.", }, money: { type: "boolean", description: "true lets the organisation see statements and earnings in scope.", }, directory: { type: "boolean", description: "true lets an administrator list or hide songs in the public directory and answer enquiries. Administrators only.", }, catalogue: { type: "string", enum: [...SCOPE_CATALOGUES], description: "whole for the whole catalogue, or chosen for only the songs and recordings named in work_ids and recording_ids.", }, work_ids: { type: "array", items: { type: "string", format: "uuid" }, maxItems: SCOPE_MAX_ITEMS, description: "Songs covered when catalogue is chosen (ids from pica_works_query). Not allowed for a master-only scope.", }, recording_ids: { type: "array", items: { type: "string", format: "uuid" }, maxItems: SCOPE_MAX_ITEMS, description: "Recordings covered when catalogue is chosen (ids from pica_recordings_query). Not allowed for a composition-only scope.", }, }, }; /** * Convert the tool's `scope` argument to the app's field names. Unknown keys * are dropped here; the schema above already refuses them at the MCP layer. */ export function scopeArgToApi(scope) { if (!scope) return undefined; const out = {}; for (const [arg, api] of Object.entries(SCOPE_FIELD_NAMES)) { if (scope[arg] !== undefined) out[api] = scope[arg]; } return out; } //# sourceMappingURL=relationship-scope.js.map