@withpica/mcp-server
Version:
MCP Server for withPICA — enables AI assistants to interact with your withPICA music catalog
126 lines • 7.52 kB
JavaScript
// Copyright (c) 2025-2026 Withpica Ltd. All rights reserved.
import { formatSuccess } from "@withpica/mcp-utils";
export class FilesTools {
pica;
constructor(pica) {
this.pica = pica;
}
getTools() {
return [
{
definition: {
name: "pica_file_deliver",
scope: "write:files",
audiences: ["stdio", "http"],
// Tier per ADR-230: `write` — this CREATES a revocable, expiring,
// recipient-scoped delivery link (an insert), not a deletion. It
// therefore does NOT warrant the `destructive:*` scope and IS
// reachable on the OAuth connector (claude.ai / ChatGPT) under
// `write:files` — agents need to "send files through chat".
//
// BUT the confirmation friction is deliberate and kept: on an IP
// platform, "send the master to john@band.com" is typo-catastrophic
// (the link is revocable, but a download that already happened can't
// be un-sent). `previewMode: "two_step_token"` forces the same
// confirmation-token round-trip the destructive tier would — without
// the destructive:* scope or the 7-day freshness gate. The first
// call returns a preview that surfaces recipient_email for the agent
// to verify; the second call echoes the token to execute.
tier: "write",
previewMode: "two_step_token",
vernacular_kind: "specialist",
vernacular_reason: "Specialist outbound-sharing context; not yet covered by the Creator Question Atlas — pending telemetry-driven addition per ADR-226 Phase 4 substrate writes. Sister of pica_notify_user.",
description: "Use when the user asks: 'send the master to john@band.com', " +
"'give the supervisor the demo', 'share this track with my manager', " +
"'email the stems to the studio'. " +
"Generate a secure, time-limited delivery link for a file PICA holds. " +
"Each delivery is bound to a specific recipient email — that recipient is " +
"the accountability party on the audit trail, and the link can be revoked " +
"via pica_file_revoke_delivery if it leaks. " +
"Returns { id, recipient_email, access_token, expires_at } — the `id` is " +
"the deliveryId for later revocation; share the access link (built from " +
"the access_token by the workspace) with the recipient. " +
"Default expiry is 48 hours; max 168 hours (1 week).",
workflows: "infrastructure",
inputSchema: {
type: "object",
properties: {
file_id: {
type: "string",
description: "UUID of the file to deliver. Get this from pica_audio_query or " +
"any inspect tool that surfaces audio file ids.",
},
recipient_email: {
type: "string",
description: "Email address of the recipient — recorded on the delivery for the " +
"audit trail. Confirm spelling with the user before calling; " +
"deliveries are revocable but the email stays on the record.",
},
expires_in_hours: {
type: "number",
description: "Hours until the link expires. Default 48. Maximum 168 (one week). " +
"Shorter expiries are better for one-off shares; longer is reasonable " +
"for ongoing collaborator access during a session.",
},
},
required: ["file_id", "recipient_email"],
},
},
executor: this.deliver.bind(this),
},
{
definition: {
name: "pica_file_revoke_delivery",
scope: "write:files",
audiences: ["stdio", "http"],
// Tier per ADR-230: `write` — kills access in place, the
// delivery record persists, fully reversible by re-issuing a
// new delivery. No new external data egress.
tier: "write",
vernacular_kind: "specialist",
vernacular_reason: "Specialist outbound-sharing context; not yet covered by the Creator Question Atlas — pending telemetry-driven addition per ADR-226 Phase 4 substrate writes. Sister of pica_notify_user.",
description: "Use when the user asks: 'cancel that link I sent to john', " +
"'revoke the delivery from yesterday', 'kill the access to the master', " +
"'they shouldn't have access anymore'. " +
"Revoke a previously-issued delivery link so the recipient can no longer " +
"use it — even if it hasn't expired yet. The delivery record stays on the " +
"audit trail for accountability; only the access is killed.",
workflows: "infrastructure",
inputSchema: {
type: "object",
properties: {
delivery_id: {
type: "string",
description: "UUID of the delivery to revoke — the `id` returned by " +
"pica_file_deliver when the link was originally created.",
},
},
required: ["delivery_id"],
},
},
executor: this.revoke.bind(this),
},
];
}
async deliver(args) {
const result = await this.pica.files.deliver({
fileId: args.file_id,
recipientEmail: args.recipient_email,
expiresIn: args.expires_in_hours,
});
return formatSuccess(`Delivery link created for ${result.recipient_email} (expires ${result.expires_at}). ` +
`Share this token-bearing URL with the recipient; revoke via pica_file_revoke_delivery if needed using id ${result.id}.`, {
delivery_id: result.id,
file_id: result.file_id,
recipient_email: result.recipient_email,
access_token: result.access_token,
expires_at: result.expires_at,
revoked: result.revoked,
});
}
async revoke(args) {
await this.pica.files.revokeDelivery(args.delivery_id);
return formatSuccess(`Delivery ${args.delivery_id} revoked. The recipient can no longer use the link.`, { delivery_id: args.delivery_id, revoked: true });
}
}
//# sourceMappingURL=files.js.map