UNPKG

@withpica/mcp-server

Version:

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

131 lines • 5.96 kB
/** * Recordings Tools — MCP tools for managing recordings. * * ADR-149 Phase 5b: Consolidated to query+inspect pattern. * - pica_recordings_query replaces pica_recordings_list + pica_recordings_by_work * - pica_recordings_inspect replaces pica_recordings_get (with sections support) * * ADR-174 Phase 2 item 3 reconciliation: the write surface covers only * real columns on `public.recordings`. Phantom fields previously * accepted via `additionalProperties: true` are gone; agents that * tried to set them silently dropped data on the way to Postgres. * * Two pre-flight surprises documented inline below: * * 1. `title` is a legacy alias for `recording_title`. The HTTP route * translates before the service sees the payload (POST since the * route was written, PATCH only since ADR-174). The tool keeps * `title` as the input field name for caller ergonomics; the * description marks the rename explicitly. * * 2. Duration lives in `duration_ms` on recordings (works carry * `duration_seconds`). Agents conflating the two get a 400 with * a redirect from the route allow-list. * * Add-candidates surfaced per the audit at * docs/audits/2026-04-14-mcp-db-reconciliation.md §3: * duration_ms, preview_url, album_art_url, spotify_url, * spotify_track_uri, spotify_track_id, youtube_video_id, youtube_url, * apple_music_url, deezer_track_id. * * ADR-282: per-society fields (ppl_registered, ppl_recording_id, * mlc_recording_id, mlc_song_code) have been replaced by the generic * society_registrations array so no single society is privileged. * * Most of these are populated by the enrichment cascade on streaming * import; exposing them lets agents seed or override that data * deliberately rather than discovering — surprised — that the field * they thought they set was silently absent. * * `isrc_prefix` is intentionally NOT in this set — column is GENERATED * ALWAYS in `public.recordings` (left(replace(isrc,'-',''),5)). Caught * by ADR-198 Phase 1 contract — every write attempt 500'd with PG * 428C9 "cannot insert non-DEFAULT value into generated column". */ import { PicaClient } from "@withpica/mcp-sdk"; import { ToolDefinition, ToolExecutor } from "./index.js"; /** * Canonical write-schema for `recordings`. Every property must * correspond to a real column on `public.recordings` (or be the * documented `title` legacy alias) — verified against * `lib/db/database.types.ts`. The HTTP route at * `app/api/admin/recordings/route.ts` carries the same allow-list * (plus the `recording_type` legacy alias for non-MCP callers); this * list is the narrower MCP-agent contract. * * `version_type` is enum-restricted by the * `recordings_version_type_check` CHECK constraint (ADR-166 Phase 1). * Reflecting the enum here means agents see invalid values as schema * errors at the tool surface, not as 500s round-tripped from Postgres. */ /** * The `recordings_version_type_check` CHECK vocabulary (ADR-166 Phase 1). * * Exported and referenced rather than re-typed, so the create/update schema * and the `pica_recordings_query` filter cannot drift apart — the filter * carried no enum at all until A10 (2026-08-24), so an agent had no way to * know `live` was rejected and `live_performance` was not. * Pinned by src/__tests__/tools/filter-param-enums.test.ts. */ export declare const RECORDING_VERSION_TYPES: readonly ["demo", "master", "alternate_master", "music_video", "lyric_video", "live_performance", "acoustic", "remix", "cover", "alternate", "instrumental", "backing_track", "edit"]; declare const RECORDING_WRITE_PROPERTIES: Record<string, Record<string, unknown>>; export declare class RecordingsTools { private pica; constructor(pica: PicaClient); /** * Get all recordings tools */ getTools(): Array<{ definition: ToolDefinition; executor: ToolExecutor; }>; /** * Query recordings — list all, search by text, or filter by work */ private queryRecordings; /** * Inspect a recording — get details with optional section filtering */ /** * Resolve an ISRC to a recording id. recordings.search filters by ISRC * client-side, so we take the first EXACT (case-insensitive, dash-stripped) * match rather than a substring hit. Throws EntityNotFoundError (→ NOT_FOUND) * when nothing matches, so inspect surfaces a hard not-found instead of a * confusing empty payload (ADR-198). */ private resolveRecordingIdByIsrc; /** * Resolve the primary recording for a section that runs WITHOUT "basic" * (e.g. sections: ["credits"]). A 404 (EntityNotFoundError) or 400 * (invalid id) on this fetch is the same hard-error class as the * basic-section fetch — the id the caller asked about cannot be * resolved — so it propagates (2026-09-01 sibling sweep of the * works_inspect 2026-07-18 fail-quiet fix; previously it degraded into * credits_error/audio_error inside a success envelope). Any other * failure is handed to onSoftError and the section records it exactly * as before — only the primary fetch gets the hard treatment, never * the section's own calls. */ private primaryForSection; private inspectRecording; /** * Create recording. * * `version_type` defaults to 'master' (the DB-level default and the * canonical ADR-166 value). Pre-ADR-174 the executor defaulted to * 'original', which is not a valid version_type — every call without * an explicit type tripped the recording-type CHECK constraint and * surfaced as a 500 from the service. */ private createRecording; /** * Update recording */ private updateRecording; /** * Delete recording */ private deleteRecording; } export { RECORDING_WRITE_PROPERTIES }; //# sourceMappingURL=recordings.d.ts.map