@withpica/mcp-server 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +5 -0
- package/CHANGELOG.md +1850 -0
- package/README.md +304 -0
- package/assets/fonts/GeistSans-Light.woff2 +0 -0
- package/assets/fonts/InstrumentSerif-Italic.woff2 +0 -0
- package/assets/fonts/InstrumentSerif-Regular.woff2 +0 -0
- package/dist/apps/download.d.ts +2 -0
- package/dist/apps/download.d.ts.map +1 -0
- package/dist/apps/download.js +125 -0
- package/dist/apps/download.js.map +1 -0
- package/dist/apps/generated/shared-bundle.d.ts +5 -0
- package/dist/apps/generated/shared-bundle.d.ts.map +1 -0
- package/dist/apps/generated/shared-bundle.js +7 -0
- package/dist/apps/generated/shared-bundle.js.map +1 -0
- package/dist/apps/release.d.ts +2 -0
- package/dist/apps/release.d.ts.map +1 -0
- package/dist/apps/release.js +69 -0
- package/dist/apps/release.js.map +1 -0
- package/dist/apps/shared.d.ts +15 -0
- package/dist/apps/shared.d.ts.map +1 -0
- package/dist/apps/shared.js +480 -0
- package/dist/apps/shared.js.map +1 -0
- package/dist/apps/upload.d.ts +2 -0
- package/dist/apps/upload.d.ts.map +1 -0
- package/dist/apps/upload.js +280 -0
- package/dist/apps/upload.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +73 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/index.d.ts +86 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +645 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/resources/agent-guide.d.ts +15 -0
- package/dist/resources/agent-guide.d.ts.map +1 -0
- package/dist/resources/agent-guide.js +284 -0
- package/dist/resources/agent-guide.js.map +1 -0
- package/dist/resources/index.d.ts +66 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +510 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/llms-primer.d.ts +2 -0
- package/dist/resources/llms-primer.d.ts.map +1 -0
- package/dist/resources/llms-primer.js +68 -0
- package/dist/resources/llms-primer.js.map +1 -0
- package/dist/resources/required-schemas.generated.d.ts +455 -0
- package/dist/resources/required-schemas.generated.d.ts.map +1 -0
- package/dist/resources/required-schemas.generated.js +1041 -0
- package/dist/resources/required-schemas.generated.js.map +1 -0
- package/dist/resources/required-schemas.source.d.ts +53 -0
- package/dist/resources/required-schemas.source.d.ts.map +1 -0
- package/dist/resources/required-schemas.source.js +493 -0
- package/dist/resources/required-schemas.source.js.map +1 -0
- package/dist/resources/welcome.d.ts +14 -0
- package/dist/resources/welcome.d.ts.map +1 -0
- package/dist/resources/welcome.js +26 -0
- package/dist/resources/welcome.js.map +1 -0
- package/dist/server-instructions.d.ts +60 -0
- package/dist/server-instructions.d.ts.map +1 -0
- package/dist/server-instructions.js +93 -0
- package/dist/server-instructions.js.map +1 -0
- package/dist/server.d.ts +52 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +334 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/access-simulate.d.ts +23 -0
- package/dist/tools/access-simulate.d.ts.map +1 -0
- package/dist/tools/access-simulate.js +162 -0
- package/dist/tools/access-simulate.js.map +1 -0
- package/dist/tools/agent-identity.d.ts +36 -0
- package/dist/tools/agent-identity.d.ts.map +1 -0
- package/dist/tools/agent-identity.js +274 -0
- package/dist/tools/agent-identity.js.map +1 -0
- package/dist/tools/agreement-types.d.ts +27 -0
- package/dist/tools/agreement-types.d.ts.map +1 -0
- package/dist/tools/agreement-types.js +281 -0
- package/dist/tools/agreement-types.js.map +1 -0
- package/dist/tools/agreements.d.ts +20 -0
- package/dist/tools/agreements.d.ts.map +1 -0
- package/dist/tools/agreements.js +282 -0
- package/dist/tools/agreements.js.map +1 -0
- package/dist/tools/analytics.d.ts +20 -0
- package/dist/tools/analytics.d.ts.map +1 -0
- package/dist/tools/analytics.js +130 -0
- package/dist/tools/analytics.js.map +1 -0
- package/dist/tools/app-tools.d.ts +15 -0
- package/dist/tools/app-tools.d.ts.map +1 -0
- package/dist/tools/app-tools.js +388 -0
- package/dist/tools/app-tools.js.map +1 -0
- package/dist/tools/assets.d.ts +25 -0
- package/dist/tools/assets.d.ts.map +1 -0
- package/dist/tools/assets.js +454 -0
- package/dist/tools/assets.js.map +1 -0
- package/dist/tools/audio-files.d.ts +20 -0
- package/dist/tools/audio-files.d.ts.map +1 -0
- package/dist/tools/audio-files.js +409 -0
- package/dist/tools/audio-files.js.map +1 -0
- package/dist/tools/audit.d.ts +19 -0
- package/dist/tools/audit.d.ts.map +1 -0
- package/dist/tools/audit.js +58 -0
- package/dist/tools/audit.js.map +1 -0
- package/dist/tools/auth.d.ts +22 -0
- package/dist/tools/auth.d.ts.map +1 -0
- package/dist/tools/auth.js +212 -0
- package/dist/tools/auth.js.map +1 -0
- package/dist/tools/bulk.d.ts +22 -0
- package/dist/tools/bulk.d.ts.map +1 -0
- package/dist/tools/bulk.js +164 -0
- package/dist/tools/bulk.js.map +1 -0
- package/dist/tools/calendar.d.ts +15 -0
- package/dist/tools/calendar.d.ts.map +1 -0
- package/dist/tools/calendar.js +68 -0
- package/dist/tools/calendar.js.map +1 -0
- package/dist/tools/collaborators.d.ts +21 -0
- package/dist/tools/collaborators.d.ts.map +1 -0
- package/dist/tools/collaborators.js +381 -0
- package/dist/tools/collaborators.js.map +1 -0
- package/dist/tools/comparisons.d.ts +22 -0
- package/dist/tools/comparisons.d.ts.map +1 -0
- package/dist/tools/comparisons.js +80 -0
- package/dist/tools/comparisons.js.map +1 -0
- package/dist/tools/credits.d.ts +39 -0
- package/dist/tools/credits.d.ts.map +1 -0
- package/dist/tools/credits.js +541 -0
- package/dist/tools/credits.js.map +1 -0
- package/dist/tools/custody-hints.d.ts +16 -0
- package/dist/tools/custody-hints.d.ts.map +1 -0
- package/dist/tools/custody-hints.js +27 -0
- package/dist/tools/custody-hints.js.map +1 -0
- package/dist/tools/custody.d.ts +38 -0
- package/dist/tools/custody.d.ts.map +1 -0
- package/dist/tools/custody.js +281 -0
- package/dist/tools/custody.js.map +1 -0
- package/dist/tools/dashboard.d.ts +22 -0
- package/dist/tools/dashboard.d.ts.map +1 -0
- package/dist/tools/dashboard.js +258 -0
- package/dist/tools/dashboard.js.map +1 -0
- package/dist/tools/directory.d.ts +15 -0
- package/dist/tools/directory.d.ts.map +1 -0
- package/dist/tools/directory.js +107 -0
- package/dist/tools/directory.js.map +1 -0
- package/dist/tools/discovery.d.ts +49 -0
- package/dist/tools/discovery.d.ts.map +1 -0
- package/dist/tools/discovery.js +851 -0
- package/dist/tools/discovery.js.map +1 -0
- package/dist/tools/disputes.d.ts +18 -0
- package/dist/tools/disputes.d.ts.map +1 -0
- package/dist/tools/disputes.js +62 -0
- package/dist/tools/disputes.js.map +1 -0
- package/dist/tools/documents.d.ts +15 -0
- package/dist/tools/documents.d.ts.map +1 -0
- package/dist/tools/documents.js +37 -0
- package/dist/tools/documents.js.map +1 -0
- package/dist/tools/duplicates.d.ts +25 -0
- package/dist/tools/duplicates.d.ts.map +1 -0
- package/dist/tools/duplicates.js +108 -0
- package/dist/tools/duplicates.js.map +1 -0
- package/dist/tools/enrichment.d.ts +56 -0
- package/dist/tools/enrichment.d.ts.map +1 -0
- package/dist/tools/enrichment.js +616 -0
- package/dist/tools/enrichment.js.map +1 -0
- package/dist/tools/exports.d.ts +19 -0
- package/dist/tools/exports.d.ts.map +1 -0
- package/dist/tools/exports.js +184 -0
- package/dist/tools/exports.js.map +1 -0
- package/dist/tools/feedback.d.ts +22 -0
- package/dist/tools/feedback.d.ts.map +1 -0
- package/dist/tools/feedback.js +100 -0
- package/dist/tools/feedback.js.map +1 -0
- package/dist/tools/forbidden-keywords.d.ts +62 -0
- package/dist/tools/forbidden-keywords.d.ts.map +1 -0
- package/dist/tools/forbidden-keywords.js +99 -0
- package/dist/tools/forbidden-keywords.js.map +1 -0
- package/dist/tools/gap-hints.d.ts +53 -0
- package/dist/tools/gap-hints.d.ts.map +1 -0
- package/dist/tools/gap-hints.js +245 -0
- package/dist/tools/gap-hints.js.map +1 -0
- package/dist/tools/groups.d.ts +29 -0
- package/dist/tools/groups.d.ts.map +1 -0
- package/dist/tools/groups.js +186 -0
- package/dist/tools/groups.js.map +1 -0
- package/dist/tools/import-documents.d.ts +21 -0
- package/dist/tools/import-documents.d.ts.map +1 -0
- package/dist/tools/import-documents.js +206 -0
- package/dist/tools/import-documents.js.map +1 -0
- package/dist/tools/import.d.ts +31 -0
- package/dist/tools/import.d.ts.map +1 -0
- package/dist/tools/import.js +610 -0
- package/dist/tools/import.js.map +1 -0
- package/dist/tools/index.d.ts +293 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +1182 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/integrations.d.ts +19 -0
- package/dist/tools/integrations.d.ts.map +1 -0
- package/dist/tools/integrations.js +120 -0
- package/dist/tools/integrations.js.map +1 -0
- package/dist/tools/labels.d.ts +20 -0
- package/dist/tools/labels.d.ts.map +1 -0
- package/dist/tools/labels.js +48 -0
- package/dist/tools/labels.js.map +1 -0
- package/dist/tools/licensing.d.ts +40 -0
- package/dist/tools/licensing.d.ts.map +1 -0
- package/dist/tools/licensing.js +436 -0
- package/dist/tools/licensing.js.map +1 -0
- package/dist/tools/memory.d.ts +21 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +120 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/metadata.d.ts +15 -0
- package/dist/tools/metadata.d.ts.map +1 -0
- package/dist/tools/metadata.js +1532 -0
- package/dist/tools/metadata.js.map +1 -0
- package/dist/tools/multimedia.d.ts +19 -0
- package/dist/tools/multimedia.d.ts.map +1 -0
- package/dist/tools/multimedia.js +371 -0
- package/dist/tools/multimedia.js.map +1 -0
- package/dist/tools/my-reported-issues.d.ts +32 -0
- package/dist/tools/my-reported-issues.d.ts.map +1 -0
- package/dist/tools/my-reported-issues.js +123 -0
- package/dist/tools/my-reported-issues.js.map +1 -0
- package/dist/tools/notes.d.ts +21 -0
- package/dist/tools/notes.d.ts.map +1 -0
- package/dist/tools/notes.js +115 -0
- package/dist/tools/notes.js.map +1 -0
- package/dist/tools/notifications.d.ts +28 -0
- package/dist/tools/notifications.d.ts.map +1 -0
- package/dist/tools/notifications.js +417 -0
- package/dist/tools/notifications.js.map +1 -0
- package/dist/tools/onboarding.d.ts +24 -0
- package/dist/tools/onboarding.d.ts.map +1 -0
- package/dist/tools/onboarding.js +81 -0
- package/dist/tools/onboarding.js.map +1 -0
- package/dist/tools/people.d.ts +254 -0
- package/dist/tools/people.d.ts.map +1 -0
- package/dist/tools/people.js +481 -0
- package/dist/tools/people.js.map +1 -0
- package/dist/tools/projects.d.ts +20 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +316 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/public-filter.d.ts +43 -0
- package/dist/tools/public-filter.d.ts.map +1 -0
- package/dist/tools/public-filter.js +356 -0
- package/dist/tools/public-filter.js.map +1 -0
- package/dist/tools/publishers.d.ts +30 -0
- package/dist/tools/publishers.d.ts.map +1 -0
- package/dist/tools/publishers.js +105 -0
- package/dist/tools/publishers.js.map +1 -0
- package/dist/tools/purchases.d.ts +27 -0
- package/dist/tools/purchases.d.ts.map +1 -0
- package/dist/tools/purchases.js +9 -0
- package/dist/tools/purchases.js.map +1 -0
- package/dist/tools/recording-attribution-hints.d.ts +24 -0
- package/dist/tools/recording-attribution-hints.d.ts.map +1 -0
- package/dist/tools/recording-attribution-hints.js +27 -0
- package/dist/tools/recording-attribution-hints.js.map +1 -0
- package/dist/tools/recordings.d.ts +96 -0
- package/dist/tools/recordings.d.ts.map +1 -0
- package/dist/tools/recordings.js +564 -0
- package/dist/tools/recordings.js.map +1 -0
- package/dist/tools/recovery-hints.d.ts +40 -0
- package/dist/tools/recovery-hints.d.ts.map +1 -0
- package/dist/tools/recovery-hints.js +827 -0
- package/dist/tools/recovery-hints.js.map +1 -0
- package/dist/tools/release-rich.d.ts +31 -0
- package/dist/tools/release-rich.d.ts.map +1 -0
- package/dist/tools/release-rich.js +245 -0
- package/dist/tools/release-rich.js.map +1 -0
- package/dist/tools/releases.d.ts +36 -0
- package/dist/tools/releases.d.ts.map +1 -0
- package/dist/tools/releases.js +649 -0
- package/dist/tools/releases.js.map +1 -0
- package/dist/tools/report-issue.d.ts +21 -0
- package/dist/tools/report-issue.d.ts.map +1 -0
- package/dist/tools/report-issue.js +101 -0
- package/dist/tools/report-issue.js.map +1 -0
- package/dist/tools/royalties.d.ts +23 -0
- package/dist/tools/royalties.d.ts.map +1 -0
- package/dist/tools/royalties.js +262 -0
- package/dist/tools/royalties.js.map +1 -0
- package/dist/tools/search.d.ts +30 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +115 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/send.d.ts +17 -0
- package/dist/tools/send.d.ts.map +1 -0
- package/dist/tools/send.js +188 -0
- package/dist/tools/send.js.map +1 -0
- package/dist/tools/sessions.d.ts +18 -0
- package/dist/tools/sessions.d.ts.map +1 -0
- package/dist/tools/sessions.js +153 -0
- package/dist/tools/sessions.js.map +1 -0
- package/dist/tools/settings.d.ts +23 -0
- package/dist/tools/settings.d.ts.map +1 -0
- package/dist/tools/settings.js +365 -0
- package/dist/tools/settings.js.map +1 -0
- package/dist/tools/share-links.d.ts +22 -0
- package/dist/tools/share-links.d.ts.map +1 -0
- package/dist/tools/share-links.js +188 -0
- package/dist/tools/share-links.js.map +1 -0
- package/dist/tools/signup.d.ts +26 -0
- package/dist/tools/signup.d.ts.map +1 -0
- package/dist/tools/signup.js +266 -0
- package/dist/tools/signup.js.map +1 -0
- package/dist/tools/split-sheets.d.ts +25 -0
- package/dist/tools/split-sheets.d.ts.map +1 -0
- package/dist/tools/split-sheets.js +309 -0
- package/dist/tools/split-sheets.js.map +1 -0
- package/dist/tools/storage-config.d.ts +13 -0
- package/dist/tools/storage-config.d.ts.map +1 -0
- package/dist/tools/storage-config.js +245 -0
- package/dist/tools/storage-config.js.map +1 -0
- package/dist/tools/subscription.d.ts +60 -0
- package/dist/tools/subscription.d.ts.map +1 -0
- package/dist/tools/subscription.js +440 -0
- package/dist/tools/subscription.js.map +1 -0
- package/dist/tools/sync-placements.d.ts +31 -0
- package/dist/tools/sync-placements.d.ts.map +1 -0
- package/dist/tools/sync-placements.js +431 -0
- package/dist/tools/sync-placements.js.map +1 -0
- package/dist/tools/team.d.ts +22 -0
- package/dist/tools/team.d.ts.map +1 -0
- package/dist/tools/team.js +144 -0
- package/dist/tools/team.js.map +1 -0
- package/dist/tools/telegram.d.ts +21 -0
- package/dist/tools/telegram.d.ts.map +1 -0
- package/dist/tools/telegram.js +144 -0
- package/dist/tools/telegram.js.map +1 -0
- package/dist/tools/uploads.d.ts +17 -0
- package/dist/tools/uploads.d.ts.map +1 -0
- package/dist/tools/uploads.js +165 -0
- package/dist/tools/uploads.js.map +1 -0
- package/dist/tools/works.d.ts +71 -0
- package/dist/tools/works.d.ts.map +1 -0
- package/dist/tools/works.js +694 -0
- package/dist/tools/works.js.map +1 -0
- package/package.json +54 -0
- package/scripts/build-required-schemas.ts +233 -0
- package/scripts/bundle-apps.ts +61 -0
- package/scripts/refresh-schema-mirror.ts +182 -0
- package/server.json +20 -0
|
@@ -0,0 +1,1182 @@
|
|
|
1
|
+
// Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
|
|
2
|
+
import { WorksTools } from "./works.js";
|
|
3
|
+
import { PeopleTools } from "./people.js";
|
|
4
|
+
import { GroupsTools } from "./groups.js";
|
|
5
|
+
import { RecordingsTools } from "./recordings.js";
|
|
6
|
+
import { SearchTools } from "./search.js";
|
|
7
|
+
import { LicensingTools } from "./licensing.js";
|
|
8
|
+
import { CreditsTools } from "./credits.js";
|
|
9
|
+
import { AudioFilesTools } from "./audio-files.js";
|
|
10
|
+
import { MultimediaTools } from "./multimedia.js";
|
|
11
|
+
import { AgreementsTools } from "./agreements.js";
|
|
12
|
+
import { MemoryTools } from "./memory.js";
|
|
13
|
+
import { EnrichmentTools } from "./enrichment.js";
|
|
14
|
+
import { BulkTools } from "./bulk.js";
|
|
15
|
+
import { ExportTools } from "./exports.js";
|
|
16
|
+
import { DuplicatesTools, coerceMergeEntityType } from "./duplicates.js";
|
|
17
|
+
import { DirectoryTools } from "./directory.js";
|
|
18
|
+
import { CollaboratorsTools } from "./collaborators.js";
|
|
19
|
+
import { ImportDocumentTools } from "./import-documents.js";
|
|
20
|
+
import { UploadTools } from "./uploads.js";
|
|
21
|
+
import { DocumentTools } from "./documents.js";
|
|
22
|
+
import { ImportTools } from "./import.js";
|
|
23
|
+
import { SendTools } from "./send.js";
|
|
24
|
+
import { ComparisonTools } from "./comparisons.js";
|
|
25
|
+
import { AssetsTools } from "./assets.js";
|
|
26
|
+
import { SessionsTools } from "./sessions.js";
|
|
27
|
+
import { AnalyticsTools } from "./analytics.js";
|
|
28
|
+
import { NotificationsTools } from "./notifications.js";
|
|
29
|
+
import { NotesTools } from "./notes.js";
|
|
30
|
+
import { TeamTools } from "./team.js";
|
|
31
|
+
import { ProjectsTools } from "./projects.js";
|
|
32
|
+
import { ReleasesTools } from "./releases.js";
|
|
33
|
+
import { ReleaseRichTools } from "./release-rich.js";
|
|
34
|
+
import { CalendarTools } from "./calendar.js";
|
|
35
|
+
import { SplitSheetsTools } from "./split-sheets.js";
|
|
36
|
+
import { AgreementTypesTools } from "./agreement-types.js";
|
|
37
|
+
import { DashboardTools } from "./dashboard.js";
|
|
38
|
+
import { IntegrationsTools } from "./integrations.js";
|
|
39
|
+
import { SettingsTools } from "./settings.js";
|
|
40
|
+
import { FeedbackTools } from "./feedback.js";
|
|
41
|
+
import { StorageConfigTools } from "./storage-config.js";
|
|
42
|
+
import { SubscriptionTools } from "./subscription.js";
|
|
43
|
+
import { OnboardingTools } from "./onboarding.js";
|
|
44
|
+
import { ReportIssueTools } from "./report-issue.js";
|
|
45
|
+
import { MyReportedIssuesTools } from "./my-reported-issues.js";
|
|
46
|
+
import { AgentIdentityTools } from "./agent-identity.js";
|
|
47
|
+
import { RoyaltiesTools } from "./royalties.js";
|
|
48
|
+
import { ShareLinksTools } from "./share-links.js";
|
|
49
|
+
import { DisputesTools } from "./disputes.js";
|
|
50
|
+
import { CustodyTools } from "./custody.js";
|
|
51
|
+
import { PurchasesTools } from "./purchases.js";
|
|
52
|
+
import { TelegramTools } from "./telegram.js";
|
|
53
|
+
import { PublishersTools } from "./publishers.js";
|
|
54
|
+
import { LabelsTools } from "./labels.js";
|
|
55
|
+
import { AuthTools } from "./auth.js";
|
|
56
|
+
import { SignupTools } from "./signup.js";
|
|
57
|
+
import { AppTools } from "./app-tools.js";
|
|
58
|
+
import { AuditTools } from "./audit.js";
|
|
59
|
+
import { DiscoveryTools } from "./discovery.js";
|
|
60
|
+
import { SyncPlacementsTools } from "./sync-placements.js";
|
|
61
|
+
import { readCredentials } from "@withpica/mcp-utils";
|
|
62
|
+
import { formatError, ToolExecutionError } from "@withpica/mcp-utils";
|
|
63
|
+
import { guardResponseSize } from "@withpica/mcp-utils";
|
|
64
|
+
import { getToolMetadata } from "./metadata.js";
|
|
65
|
+
import { getRecoveryHint } from "./recovery-hints.js";
|
|
66
|
+
import { generateConfirmationToken, validateAndConsumeToken, } from "@withpica/mcp-utils";
|
|
67
|
+
import { buildSessionState, incrementSessionMutations, resetSinceLastBriefing, } from "@withpica/mcp-utils";
|
|
68
|
+
/**
|
|
69
|
+
* Build a tool description with metadata injected.
|
|
70
|
+
* Format: "[category] original description"
|
|
71
|
+
*/
|
|
72
|
+
export function injectMetadataIntoDescription(description, metadata) {
|
|
73
|
+
// Category tag helps the agent understand tool grouping.
|
|
74
|
+
// Risk prefixes removed — annotations (readOnlyHint, destructiveHint) handle this structurally.
|
|
75
|
+
// Behavioral guidance (tell user what you're doing) is in server instructions (one copy).
|
|
76
|
+
return `[${metadata.category}] ${description}`;
|
|
77
|
+
}
|
|
78
|
+
export const CATEGORY_DISPLAY = {
|
|
79
|
+
catalog: "manage your catalog",
|
|
80
|
+
enrichment: "enrich your metadata",
|
|
81
|
+
business: "handle business and agreements",
|
|
82
|
+
discovery: "search and explore",
|
|
83
|
+
media: "manage photos, audio, and video",
|
|
84
|
+
comms: "send and share",
|
|
85
|
+
settings: "manage your account and team",
|
|
86
|
+
};
|
|
87
|
+
export class ToolRegistry {
|
|
88
|
+
tools;
|
|
89
|
+
pica;
|
|
90
|
+
config;
|
|
91
|
+
reinitializeCallback;
|
|
92
|
+
signOutCallback;
|
|
93
|
+
auditLogger;
|
|
94
|
+
callerContext;
|
|
95
|
+
constructor(pica, config, reinitializeCallback, callerContext, signOutCallback) {
|
|
96
|
+
this.pica = pica;
|
|
97
|
+
this.config = config;
|
|
98
|
+
this.reinitializeCallback = reinitializeCallback;
|
|
99
|
+
this.signOutCallback = signOutCallback;
|
|
100
|
+
this.callerContext = callerContext ?? {
|
|
101
|
+
callerIdentity: "unknown",
|
|
102
|
+
transport: "stdio",
|
|
103
|
+
};
|
|
104
|
+
this.tools = new Map();
|
|
105
|
+
this.registerAllTools();
|
|
106
|
+
}
|
|
107
|
+
setAuditLogger(logger) {
|
|
108
|
+
this.auditLogger = logger;
|
|
109
|
+
}
|
|
110
|
+
setCallerContext(context) {
|
|
111
|
+
this.callerContext = context;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Register all available tools
|
|
115
|
+
*/
|
|
116
|
+
registerAllTools() {
|
|
117
|
+
// Auth tools — always registered (lobby + authenticated mode)
|
|
118
|
+
if (this.config) {
|
|
119
|
+
const authTools = new AuthTools(this.config, this.reinitializeCallback || (() => { }), this.signOutCallback || (() => { }));
|
|
120
|
+
authTools.getTools().forEach((tool) => {
|
|
121
|
+
this.tools.set(tool.definition.name, tool);
|
|
122
|
+
});
|
|
123
|
+
// ADR-211 P1 — Signup tool. Registered in lobby alongside the auth
|
|
124
|
+
// pair so unauthenticated callers (stdio without API key, HTTP
|
|
125
|
+
// without Bearer) have a discoverable signup verb. Stays registered
|
|
126
|
+
// post-auth too — the tool itself is harmless once you're signed in
|
|
127
|
+
// (mints a link for a new account, not your existing one) and that
|
|
128
|
+
// matches the ADR-211 § Primitive A 2026-04-30 amendment which
|
|
129
|
+
// makes Phase 1 lobby additive (3 tools), not replacement.
|
|
130
|
+
//
|
|
131
|
+
// Transport is read from callerContext so stdio + HTTP transports
|
|
132
|
+
// stamp their JWTs correctly without per-call config plumbing.
|
|
133
|
+
const signupTools = new SignupTools({
|
|
134
|
+
apiUrl: this.config.picaApiUrl,
|
|
135
|
+
transport: this.callerContext.transport,
|
|
136
|
+
});
|
|
137
|
+
signupTools.getTools().forEach((tool) => {
|
|
138
|
+
this.tools.set(tool.definition.name, tool);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// In lobby mode, only auth + signup tools are available
|
|
142
|
+
if (this.config?.lobbyMode || !this.pica) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const pica = this.pica;
|
|
146
|
+
// Works tools
|
|
147
|
+
const worksTools = new WorksTools(pica);
|
|
148
|
+
worksTools.getTools().forEach((tool) => {
|
|
149
|
+
this.tools.set(tool.definition.name, tool);
|
|
150
|
+
});
|
|
151
|
+
// People tools
|
|
152
|
+
const peopleTools = new PeopleTools(pica);
|
|
153
|
+
peopleTools.getTools().forEach((tool) => {
|
|
154
|
+
this.tools.set(tool.definition.name, tool);
|
|
155
|
+
});
|
|
156
|
+
// Groups tools (ADR-223)
|
|
157
|
+
const groupsTools = new GroupsTools(pica);
|
|
158
|
+
groupsTools.getTools().forEach((tool) => {
|
|
159
|
+
this.tools.set(tool.definition.name, tool);
|
|
160
|
+
});
|
|
161
|
+
// Recordings tools
|
|
162
|
+
const recordingsTools = new RecordingsTools(pica);
|
|
163
|
+
recordingsTools.getTools().forEach((tool) => {
|
|
164
|
+
this.tools.set(tool.definition.name, tool);
|
|
165
|
+
});
|
|
166
|
+
// Search tools
|
|
167
|
+
const searchTools = new SearchTools(pica);
|
|
168
|
+
searchTools.getTools().forEach((tool) => {
|
|
169
|
+
this.tools.set(tool.definition.name, tool);
|
|
170
|
+
});
|
|
171
|
+
// Licensing tools
|
|
172
|
+
const licensingTools = new LicensingTools(pica);
|
|
173
|
+
licensingTools.getTools().forEach((tool) => {
|
|
174
|
+
this.tools.set(tool.definition.name, tool);
|
|
175
|
+
});
|
|
176
|
+
// Credits tools
|
|
177
|
+
const creditsTools = new CreditsTools(pica);
|
|
178
|
+
creditsTools.getTools().forEach((tool) => {
|
|
179
|
+
this.tools.set(tool.definition.name, tool);
|
|
180
|
+
});
|
|
181
|
+
// Audio Files tools
|
|
182
|
+
const audioFilesTools = new AudioFilesTools(pica);
|
|
183
|
+
audioFilesTools.getTools().forEach((tool) => {
|
|
184
|
+
this.tools.set(tool.definition.name, tool);
|
|
185
|
+
});
|
|
186
|
+
// Multimedia tools
|
|
187
|
+
const multimediaTools = new MultimediaTools(pica);
|
|
188
|
+
multimediaTools.getTools().forEach((tool) => {
|
|
189
|
+
this.tools.set(tool.definition.name, tool);
|
|
190
|
+
});
|
|
191
|
+
// Agreements tools
|
|
192
|
+
const agreementsTools = new AgreementsTools(pica);
|
|
193
|
+
agreementsTools.getTools().forEach((tool) => {
|
|
194
|
+
this.tools.set(tool.definition.name, tool);
|
|
195
|
+
});
|
|
196
|
+
// Memory tools
|
|
197
|
+
const memoryTools = new MemoryTools(pica);
|
|
198
|
+
memoryTools.getTools().forEach((tool) => {
|
|
199
|
+
this.tools.set(tool.definition.name, tool);
|
|
200
|
+
});
|
|
201
|
+
// Enrichment tools
|
|
202
|
+
const enrichmentTools = new EnrichmentTools(pica);
|
|
203
|
+
enrichmentTools.getTools().forEach((tool) => {
|
|
204
|
+
this.tools.set(tool.definition.name, tool);
|
|
205
|
+
});
|
|
206
|
+
// Bulk operations tools
|
|
207
|
+
const bulkTools = new BulkTools(pica);
|
|
208
|
+
bulkTools.getTools().forEach((tool) => {
|
|
209
|
+
this.tools.set(tool.definition.name, tool);
|
|
210
|
+
});
|
|
211
|
+
// Export tools
|
|
212
|
+
const exportTools = new ExportTools(pica);
|
|
213
|
+
exportTools.getTools().forEach((tool) => {
|
|
214
|
+
this.tools.set(tool.definition.name, tool);
|
|
215
|
+
});
|
|
216
|
+
// Duplicates tools
|
|
217
|
+
const duplicatesTools = new DuplicatesTools(pica);
|
|
218
|
+
duplicatesTools.getTools().forEach((tool) => {
|
|
219
|
+
this.tools.set(tool.definition.name, tool);
|
|
220
|
+
});
|
|
221
|
+
// Directory tools
|
|
222
|
+
const directoryTools = new DirectoryTools(pica);
|
|
223
|
+
directoryTools.getTools().forEach((tool) => {
|
|
224
|
+
this.tools.set(tool.definition.name, tool);
|
|
225
|
+
});
|
|
226
|
+
// Collaborator tools
|
|
227
|
+
const collaboratorsTools = new CollaboratorsTools(pica);
|
|
228
|
+
collaboratorsTools.getTools().forEach((tool) => {
|
|
229
|
+
this.tools.set(tool.definition.name, tool);
|
|
230
|
+
});
|
|
231
|
+
// Upload tools (generic file upload — images, videos, documents)
|
|
232
|
+
const uploadTools = new UploadTools(pica);
|
|
233
|
+
uploadTools.getTools().forEach((tool) => {
|
|
234
|
+
this.tools.set(tool.definition.name, tool);
|
|
235
|
+
});
|
|
236
|
+
// Document tools (AI analysis)
|
|
237
|
+
const documentTools = new DocumentTools(pica);
|
|
238
|
+
documentTools.getTools().forEach((tool) => {
|
|
239
|
+
this.tools.set(tool.definition.name, tool);
|
|
240
|
+
});
|
|
241
|
+
// Bulk import tools (CSV import pipeline)
|
|
242
|
+
const importTools = new ImportTools(pica);
|
|
243
|
+
importTools.getTools().forEach((tool) => {
|
|
244
|
+
this.tools.set(tool.definition.name, tool);
|
|
245
|
+
});
|
|
246
|
+
// Comparison tools (ADR-116 Phase 2: enrichment + registration comparison)
|
|
247
|
+
const comparisonTools = new ComparisonTools(pica);
|
|
248
|
+
comparisonTools.getTools().forEach((tool) => {
|
|
249
|
+
this.tools.set(tool.definition.name, tool);
|
|
250
|
+
});
|
|
251
|
+
// Send Hub tools (messages, invites, file requests, audio shares)
|
|
252
|
+
const sendTools = new SendTools(pica);
|
|
253
|
+
sendTools.getTools().forEach((tool) => {
|
|
254
|
+
this.tools.set(tool.definition.name, tool);
|
|
255
|
+
});
|
|
256
|
+
// Import document tools
|
|
257
|
+
const importDocumentTools = new ImportDocumentTools(pica);
|
|
258
|
+
importDocumentTools.getTools().forEach((tool) => {
|
|
259
|
+
this.tools.set(tool.definition.name, tool);
|
|
260
|
+
});
|
|
261
|
+
// Physical assets tools (equipment, instruments, studio gear)
|
|
262
|
+
const assetsTools = new AssetsTools(pica);
|
|
263
|
+
assetsTools.getTools().forEach((tool) => {
|
|
264
|
+
this.tools.set(tool.definition.name, tool);
|
|
265
|
+
});
|
|
266
|
+
// Work sessions tools (studio session tracking)
|
|
267
|
+
const sessionsTools = new SessionsTools(pica);
|
|
268
|
+
sessionsTools.getTools().forEach((tool) => {
|
|
269
|
+
this.tools.set(tool.definition.name, tool);
|
|
270
|
+
});
|
|
271
|
+
// Analytics tools (carbon, provenance, diligence)
|
|
272
|
+
const analyticsTools = new AnalyticsTools(pica);
|
|
273
|
+
analyticsTools.getTools().forEach((tool) => {
|
|
274
|
+
this.tools.set(tool.definition.name, tool);
|
|
275
|
+
});
|
|
276
|
+
// Notifications tools
|
|
277
|
+
const notificationsTools = new NotificationsTools(pica);
|
|
278
|
+
notificationsTools.getTools().forEach((tool) => {
|
|
279
|
+
this.tools.set(tool.definition.name, tool);
|
|
280
|
+
});
|
|
281
|
+
// Notes tools (catalog notes on works, people, general observations)
|
|
282
|
+
const notesTools = new NotesTools(pica);
|
|
283
|
+
notesTools.getTools().forEach((tool) => {
|
|
284
|
+
this.tools.set(tool.definition.name, tool);
|
|
285
|
+
});
|
|
286
|
+
// Team management tools (invite, list, update, remove members)
|
|
287
|
+
const teamTools = new TeamTools(pica);
|
|
288
|
+
teamTools.getTools().forEach((tool) => {
|
|
289
|
+
this.tools.set(tool.definition.name, tool);
|
|
290
|
+
});
|
|
291
|
+
// Projects tools (albums, EPs, compilations — grouping works)
|
|
292
|
+
const projectsTools = new ProjectsTools(pica);
|
|
293
|
+
projectsTools.getTools().forEach((tool) => {
|
|
294
|
+
this.tools.set(tool.definition.name, tool);
|
|
295
|
+
});
|
|
296
|
+
// Releases tools (albums, EPs, singles with dates, labels, UPCs)
|
|
297
|
+
const releasesTools = new ReleasesTools(pica);
|
|
298
|
+
releasesTools.getTools().forEach((tool) => {
|
|
299
|
+
this.tools.set(tool.definition.name, tool);
|
|
300
|
+
});
|
|
301
|
+
// ADR-208 Phase 3 Primitive D — release rich render (image + markdown)
|
|
302
|
+
const releaseRichTools = new ReleaseRichTools(pica);
|
|
303
|
+
releaseRichTools.getTools().forEach((tool) => {
|
|
304
|
+
this.tools.set(tool.definition.name, tool);
|
|
305
|
+
});
|
|
306
|
+
// Calendar tools
|
|
307
|
+
const calendarTools = new CalendarTools(pica);
|
|
308
|
+
calendarTools.getTools().forEach((tool) => {
|
|
309
|
+
this.tools.set(tool.definition.name, tool);
|
|
310
|
+
});
|
|
311
|
+
// Split sheets & recording splits tools
|
|
312
|
+
const splitSheetsTools = new SplitSheetsTools(pica);
|
|
313
|
+
splitSheetsTools.getTools().forEach((tool) => {
|
|
314
|
+
this.tools.set(tool.definition.name, tool);
|
|
315
|
+
});
|
|
316
|
+
// Agreement types tools (templates, producer agreements, work-for-hire)
|
|
317
|
+
const agreementTypesTools = new AgreementTypesTools(pica);
|
|
318
|
+
agreementTypesTools.getTools().forEach((tool) => {
|
|
319
|
+
this.tools.set(tool.definition.name, tool);
|
|
320
|
+
});
|
|
321
|
+
// Dashboard & discovery tools
|
|
322
|
+
const dashboardTools = new DashboardTools(pica);
|
|
323
|
+
dashboardTools.getTools().forEach((tool) => {
|
|
324
|
+
this.tools.set(tool.definition.name, tool);
|
|
325
|
+
});
|
|
326
|
+
// Integrations tools (connection status)
|
|
327
|
+
const integrationsTools = new IntegrationsTools(pica);
|
|
328
|
+
integrationsTools.getTools().forEach((tool) => {
|
|
329
|
+
this.tools.set(tool.definition.name, tool);
|
|
330
|
+
});
|
|
331
|
+
// Settings tools (credits history, storage, org profile)
|
|
332
|
+
const settingsTools = new SettingsTools(pica);
|
|
333
|
+
settingsTools.getTools().forEach((tool) => {
|
|
334
|
+
this.tools.set(tool.definition.name, tool);
|
|
335
|
+
});
|
|
336
|
+
// Feedback tools (ADR-184 slice 8 — pica_submit_feedback)
|
|
337
|
+
const feedbackTools = new FeedbackTools(pica);
|
|
338
|
+
feedbackTools.getTools().forEach((tool) => {
|
|
339
|
+
this.tools.set(tool.definition.name, tool);
|
|
340
|
+
});
|
|
341
|
+
// Storage config tools (ADR-217 — Primitives B + C; Primitive A
|
|
342
|
+
// pica_storage_status lives in settings.ts because it shipped earlier
|
|
343
|
+
// alongside the original settings cluster).
|
|
344
|
+
const storageConfigTools = new StorageConfigTools(pica);
|
|
345
|
+
storageConfigTools.getTools().forEach((tool) => {
|
|
346
|
+
this.tools.set(tool.definition.name, tool);
|
|
347
|
+
});
|
|
348
|
+
// Subscription tools (ADR-210 Phase 2 — pica_subscription_status +
|
|
349
|
+
// pica_subscription_manage). Explicit billing read + action surface
|
|
350
|
+
// complementing the ambient _meta.session_state.billing_slice from
|
|
351
|
+
// Phase 1.
|
|
352
|
+
const subscriptionTools = new SubscriptionTools(pica);
|
|
353
|
+
subscriptionTools.getTools().forEach((tool) => {
|
|
354
|
+
this.tools.set(tool.definition.name, tool);
|
|
355
|
+
});
|
|
356
|
+
// Onboarding tools — pica_acknowledge_onboarding gates OAuth-arrived
|
|
357
|
+
// users until the agent walks them through identity/security/intent.
|
|
358
|
+
const onboardingTools = new OnboardingTools(pica);
|
|
359
|
+
onboardingTools.getTools().forEach((tool) => {
|
|
360
|
+
this.tools.set(tool.definition.name, tool);
|
|
361
|
+
});
|
|
362
|
+
// Report-issue tool (ADR-196 Phase 3 — pica_report_issue)
|
|
363
|
+
const reportIssueTools = new ReportIssueTools(pica);
|
|
364
|
+
reportIssueTools.getTools().forEach((tool) => {
|
|
365
|
+
this.tools.set(tool.definition.name, tool);
|
|
366
|
+
});
|
|
367
|
+
// My-reported-issues tool (ADR-196 Phase 4a — pica_my_reported_issues)
|
|
368
|
+
// — closed-loop counterpart to pica_report_issue. Org-scoped polling.
|
|
369
|
+
const myReportedIssuesTools = new MyReportedIssuesTools(pica);
|
|
370
|
+
myReportedIssuesTools.getTools().forEach((tool) => {
|
|
371
|
+
this.tools.set(tool.definition.name, tool);
|
|
372
|
+
});
|
|
373
|
+
// Agent identity tools (ADR-185 Part 1 — session-auth-only MCP surface).
|
|
374
|
+
// The three tools refuse grant-auth callers at the HTTP API layer;
|
|
375
|
+
// the stdio path carries an API key so behaviour is identical either
|
|
376
|
+
// way.
|
|
377
|
+
const agentIdentityTools = new AgentIdentityTools(pica);
|
|
378
|
+
agentIdentityTools.getTools().forEach((tool) => {
|
|
379
|
+
this.tools.set(tool.definition.name, tool);
|
|
380
|
+
});
|
|
381
|
+
// Royalties & statements tools (ADR-139 Step 2)
|
|
382
|
+
const royaltiesTools = new RoyaltiesTools(pica);
|
|
383
|
+
royaltiesTools.getTools().forEach((tool) => {
|
|
384
|
+
this.tools.set(tool.definition.name, tool);
|
|
385
|
+
});
|
|
386
|
+
// Share links tools (ADR-139 Step 3)
|
|
387
|
+
const shareLinksTools = new ShareLinksTools(pica);
|
|
388
|
+
shareLinksTools.getTools().forEach((tool) => {
|
|
389
|
+
this.tools.set(tool.definition.name, tool);
|
|
390
|
+
});
|
|
391
|
+
// Disputes tools (ADR-139 Step 3)
|
|
392
|
+
const disputesTools = new DisputesTools(pica);
|
|
393
|
+
disputesTools.getTools().forEach((tool) => {
|
|
394
|
+
this.tools.set(tool.definition.name, tool);
|
|
395
|
+
});
|
|
396
|
+
// Custody tools (ADR-158 Plan C)
|
|
397
|
+
const custodyTools = new CustodyTools(pica);
|
|
398
|
+
custodyTools.getTools().forEach((tool) => {
|
|
399
|
+
this.tools.set(tool.definition.name, tool);
|
|
400
|
+
});
|
|
401
|
+
// Purchases tools (credit checkout)
|
|
402
|
+
const purchasesTools = new PurchasesTools(pica);
|
|
403
|
+
purchasesTools.getTools().forEach((tool) => {
|
|
404
|
+
this.tools.set(tool.definition.name, tool);
|
|
405
|
+
});
|
|
406
|
+
// Telegram tools (user notification channel)
|
|
407
|
+
const telegramTools = new TelegramTools(pica);
|
|
408
|
+
telegramTools.getTools().forEach((tool) => {
|
|
409
|
+
this.tools.set(tool.definition.name, tool);
|
|
410
|
+
});
|
|
411
|
+
// Publishers tools (lookup + create)
|
|
412
|
+
const publishersTools = new PublishersTools(pica);
|
|
413
|
+
publishersTools.getTools().forEach((tool) => {
|
|
414
|
+
this.tools.set(tool.definition.name, tool);
|
|
415
|
+
});
|
|
416
|
+
// Labels tools (read-only lookup; ADR-174 Decision 3)
|
|
417
|
+
const labelsTools = new LabelsTools(pica);
|
|
418
|
+
labelsTools.getTools().forEach((tool) => {
|
|
419
|
+
this.tools.set(tool.definition.name, tool);
|
|
420
|
+
});
|
|
421
|
+
// App tools (MCP Apps — interactive UI cards)
|
|
422
|
+
const appTools = new AppTools(pica);
|
|
423
|
+
appTools.getTools().forEach((tool) => {
|
|
424
|
+
this.tools.set(tool.definition.name, tool);
|
|
425
|
+
});
|
|
426
|
+
// Audit tools (recent MCP tool executions — ADR-154 F6)
|
|
427
|
+
// ADR-181 Phase 1: ported from the (now-retired)
|
|
428
|
+
// @withpica/mcp-server-settings sub-server as part of the customer
|
|
429
|
+
// MCP surface consolidation. The sub-server was the only place this
|
|
430
|
+
// tool shipped before consolidation; every other sub-server tool
|
|
431
|
+
// already existed in mcp-server/ so only pica_audit_list had to move.
|
|
432
|
+
// Phase 3 (2026-04-19) tombstoned the sub-package on npm + registry
|
|
433
|
+
// and deleted the source tree.
|
|
434
|
+
const auditTools = new AuditTools(pica);
|
|
435
|
+
auditTools.getTools().forEach((tool) => {
|
|
436
|
+
this.tools.set(tool.definition.name, tool);
|
|
437
|
+
});
|
|
438
|
+
// ADR-222 sync placements
|
|
439
|
+
const syncPlacementsTools = new SyncPlacementsTools(pica);
|
|
440
|
+
syncPlacementsTools.getTools().forEach((tool) => {
|
|
441
|
+
this.tools.set(tool.definition.name, tool);
|
|
442
|
+
});
|
|
443
|
+
// Discovery meta-tools — registered last so pica_tool_details can look up
|
|
444
|
+
// all tool definitions. Only active when discoveryMode is enabled.
|
|
445
|
+
if (this.config?.discoveryMode) {
|
|
446
|
+
const discoveryTools = new DiscoveryTools((name, args, ctx) => this.executeTool(name, args, ctx), (name) => this.tools.get(name));
|
|
447
|
+
discoveryTools.getTools().forEach((tool) => {
|
|
448
|
+
this.tools.set(tool.definition.name, tool);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// ── Write-safety classification ──
|
|
453
|
+
// Destructive tools require confirmation before AND summary after.
|
|
454
|
+
// Mutating tools should present what they'll do and confirm after.
|
|
455
|
+
// Safe (read-only) tools need no confirmation.
|
|
456
|
+
static DESTRUCTIVE_PATTERNS = [
|
|
457
|
+
"_delete",
|
|
458
|
+
"_bulk_delete",
|
|
459
|
+
"_merge",
|
|
460
|
+
"_remove",
|
|
461
|
+
"_disconnect",
|
|
462
|
+
];
|
|
463
|
+
static MUTATING_PATTERNS = [
|
|
464
|
+
"_create",
|
|
465
|
+
"_update",
|
|
466
|
+
"_bulk_update",
|
|
467
|
+
"_invite",
|
|
468
|
+
"_link",
|
|
469
|
+
"_send_",
|
|
470
|
+
"_import_",
|
|
471
|
+
"_execute",
|
|
472
|
+
"_ingest",
|
|
473
|
+
"_enrich",
|
|
474
|
+
"_verify",
|
|
475
|
+
"_mark_",
|
|
476
|
+
"_review",
|
|
477
|
+
"_purchase",
|
|
478
|
+
"_submit",
|
|
479
|
+
"_generate",
|
|
480
|
+
"_set_default",
|
|
481
|
+
"_duplicate",
|
|
482
|
+
"_toggle",
|
|
483
|
+
"_save",
|
|
484
|
+
"_upload",
|
|
485
|
+
"_complete",
|
|
486
|
+
"_analyze",
|
|
487
|
+
"_analyse",
|
|
488
|
+
"_identify",
|
|
489
|
+
"_resend",
|
|
490
|
+
"_notify",
|
|
491
|
+
];
|
|
492
|
+
// Read-only tools that match mutating patterns by name but are actually safe
|
|
493
|
+
static SAFE_OVERRIDES = new Set([
|
|
494
|
+
"pica_collaborators_invites_list",
|
|
495
|
+
"pica_enrichment_candidates",
|
|
496
|
+
"pica_enrichment_compare",
|
|
497
|
+
"pica_find_duplicates",
|
|
498
|
+
"pica_import_analyze",
|
|
499
|
+
"pica_import_validate",
|
|
500
|
+
"pica_import_fields",
|
|
501
|
+
"pica_import_template",
|
|
502
|
+
"pica_import_documents_query",
|
|
503
|
+
"pica_import_documents_inspect",
|
|
504
|
+
"pica_send_query",
|
|
505
|
+
"pica_share_links_list",
|
|
506
|
+
]);
|
|
507
|
+
classifyTool(name) {
|
|
508
|
+
if (ToolRegistry.SAFE_OVERRIDES.has(name)) {
|
|
509
|
+
return "safe";
|
|
510
|
+
}
|
|
511
|
+
if (ToolRegistry.DESTRUCTIVE_PATTERNS.some((p) => name.includes(p))) {
|
|
512
|
+
return "destructive";
|
|
513
|
+
}
|
|
514
|
+
if (ToolRegistry.MUTATING_PATTERNS.some((p) => name.includes(p))) {
|
|
515
|
+
return "mutating";
|
|
516
|
+
}
|
|
517
|
+
return "safe";
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* List all available tools with write-safety prefixes injected.
|
|
521
|
+
* When discoveryMode is enabled, only the 5 handshake-visible tools are returned.
|
|
522
|
+
* All other tools remain registered and callable via pica_execute.
|
|
523
|
+
*/
|
|
524
|
+
listTools() {
|
|
525
|
+
let toolsToList = Array.from(this.tools.values());
|
|
526
|
+
if (this.config?.discoveryMode) {
|
|
527
|
+
const META_TOOL_NAMES = new Set([
|
|
528
|
+
"pica_sign_in",
|
|
529
|
+
"pica_sign_out",
|
|
530
|
+
"pica_discover",
|
|
531
|
+
"pica_tool_details",
|
|
532
|
+
"pica_execute",
|
|
533
|
+
]);
|
|
534
|
+
toolsToList = toolsToList.filter((t) => META_TOOL_NAMES.has(t.definition.name));
|
|
535
|
+
}
|
|
536
|
+
return toolsToList.map((tool) => {
|
|
537
|
+
let definition = tool.definition;
|
|
538
|
+
const metadata = getToolMetadata(definition.name);
|
|
539
|
+
// Inject confirmation_token into destructive tool schemas
|
|
540
|
+
const isDestructive = metadata?.risk === "destructive" ||
|
|
541
|
+
(!metadata && this.classifyTool(definition.name) === "destructive");
|
|
542
|
+
if (isDestructive) {
|
|
543
|
+
definition = {
|
|
544
|
+
...definition,
|
|
545
|
+
inputSchema: {
|
|
546
|
+
...definition.inputSchema,
|
|
547
|
+
properties: {
|
|
548
|
+
...definition.inputSchema.properties,
|
|
549
|
+
confirmation_token: {
|
|
550
|
+
type: "string",
|
|
551
|
+
description: "Confirmation token from a previous call. Required to execute destructive operations. " +
|
|
552
|
+
"Call this tool once without a token to get a preview and token, then call again with the token to confirm.",
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
// Normalize _meta.ui.resourceUri → legacy "ui/resourceUri" key for older MCP Apps clients
|
|
559
|
+
if (definition._meta?.ui?.resourceUri &&
|
|
560
|
+
!definition._meta["ui/resourceUri"]) {
|
|
561
|
+
definition = {
|
|
562
|
+
...definition,
|
|
563
|
+
_meta: {
|
|
564
|
+
...definition._meta,
|
|
565
|
+
"ui/resourceUri": definition._meta.ui.resourceUri,
|
|
566
|
+
},
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
if (metadata) {
|
|
570
|
+
return {
|
|
571
|
+
...definition,
|
|
572
|
+
description: injectMetadataIntoDescription(tool.definition.description, metadata),
|
|
573
|
+
annotations: {
|
|
574
|
+
title: metadata.display_name,
|
|
575
|
+
readOnlyHint: metadata.risk === "safe",
|
|
576
|
+
destructiveHint: metadata.risk === "destructive",
|
|
577
|
+
idempotentHint: metadata.risk !== "destructive" && metadata.retry_safe,
|
|
578
|
+
openWorldHint: false,
|
|
579
|
+
// ADR-199 extended annotation classes — read by ChatGPT/Claude.ai
|
|
580
|
+
// connectors directly. Derived from existing metadata so existing
|
|
581
|
+
// tools get this for free without per-tool edits.
|
|
582
|
+
...this.deriveExtendedAnnotations(definition, metadata),
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
// Fallback to old pattern-matching for any tool without explicit metadata
|
|
587
|
+
const classification = this.classifyTool(definition.name);
|
|
588
|
+
const annotations = {
|
|
589
|
+
title: definition.name,
|
|
590
|
+
readOnlyHint: classification === "safe",
|
|
591
|
+
destructiveHint: classification === "destructive",
|
|
592
|
+
idempotentHint: false,
|
|
593
|
+
openWorldHint: false,
|
|
594
|
+
// ADR-199: derive the extended classes even without explicit
|
|
595
|
+
// metadata so the fallback tools also surface category/risk to
|
|
596
|
+
// ChatGPT/Claude.ai connectors.
|
|
597
|
+
...this.deriveExtendedAnnotations(definition, null, classification),
|
|
598
|
+
};
|
|
599
|
+
// No risk prefix needed — annotations handle this structurally
|
|
600
|
+
return { ...definition, annotations };
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* ADR-199 — derive the extended annotation classes (riskLevel, category,
|
|
605
|
+
* requiresConfirmation, createsExternalSideEffects, returnsPaginated,
|
|
606
|
+
* requiresFollowUpInspection) from existing metadata + tool name patterns.
|
|
607
|
+
*
|
|
608
|
+
* Called from listTools() for every tool. Pure derivation: zero per-tool
|
|
609
|
+
* edits required for the retrofit. Future tool authors can override by
|
|
610
|
+
* setting fields directly on definition.annotations.
|
|
611
|
+
*/
|
|
612
|
+
deriveExtendedAnnotations(definition, metadata, classification) {
|
|
613
|
+
const name = definition.name;
|
|
614
|
+
const risk = metadata?.risk ?? classification ?? "safe";
|
|
615
|
+
// Side-effecting external systems: telegram, send-hub, comms, broadcasts.
|
|
616
|
+
// Pattern-derived so new tools inherit without edits.
|
|
617
|
+
const externalSideEffectPatterns = [
|
|
618
|
+
"telegram",
|
|
619
|
+
"_send_",
|
|
620
|
+
"_send",
|
|
621
|
+
"send_message",
|
|
622
|
+
"send_resend",
|
|
623
|
+
"team_comms_send",
|
|
624
|
+
"_notify_user",
|
|
625
|
+
"_invite",
|
|
626
|
+
];
|
|
627
|
+
const createsExternalSideEffects = externalSideEffectPatterns.some((p) => name.includes(p));
|
|
628
|
+
// Paginated reads: every *_query and *_list tool.
|
|
629
|
+
const returnsPaginated = /_query$|_list$|_search/.test(name);
|
|
630
|
+
// Tools that mutate state are followed by a verifying inspect call when
|
|
631
|
+
// the agent wants to confirm — the gap report flagged this as a useful
|
|
632
|
+
// class for ChatGPT to surface in the UI.
|
|
633
|
+
const requiresFollowUpInspection = risk === "mutating" || risk === "destructive";
|
|
634
|
+
return {
|
|
635
|
+
riskLevel: risk,
|
|
636
|
+
category: metadata?.category,
|
|
637
|
+
requiresConfirmation: risk === "destructive",
|
|
638
|
+
createsExternalSideEffects,
|
|
639
|
+
returnsPaginated,
|
|
640
|
+
requiresFollowUpInspection,
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Build a human-readable preview of what a destructive operation will affect.
|
|
645
|
+
*/
|
|
646
|
+
async buildDestructivePreview(name, args) {
|
|
647
|
+
const metadata = getToolMetadata(name);
|
|
648
|
+
const displayName = metadata?.display_name || name;
|
|
649
|
+
// buildDestructivePreview is only called during tool execution,
|
|
650
|
+
// which is gated behind lobby mode check — pica is always available here.
|
|
651
|
+
const pica = this.pica;
|
|
652
|
+
try {
|
|
653
|
+
switch (name) {
|
|
654
|
+
case "pica_works_delete": {
|
|
655
|
+
const work = await pica.works.get(args.id).catch(() => null);
|
|
656
|
+
const title = work?.title || args.id;
|
|
657
|
+
return {
|
|
658
|
+
action: "delete work",
|
|
659
|
+
target: title,
|
|
660
|
+
warning: "this will remove linked credits, agreement links, and audio files. recordings will be unlinked. this cannot be undone for works without verified identifiers.",
|
|
661
|
+
display_name: displayName,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
case "pica_works_bulk_delete": {
|
|
665
|
+
const count = args.ids?.length || 0;
|
|
666
|
+
return {
|
|
667
|
+
action: "bulk delete works",
|
|
668
|
+
target: `${count} works`,
|
|
669
|
+
warning: `this will delete ${count} work(s) and cascade to their credits and agreement links. this cannot be undone for works without verified identifiers.`,
|
|
670
|
+
display_name: displayName,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
case "pica_people_delete": {
|
|
674
|
+
const person = await pica.people.get(args.id).catch(() => null);
|
|
675
|
+
const personName = person?.first_name
|
|
676
|
+
? `${person.first_name} ${person.last_name || ""}`.trim()
|
|
677
|
+
: args.id;
|
|
678
|
+
return {
|
|
679
|
+
action: "remove person",
|
|
680
|
+
target: personName,
|
|
681
|
+
warning: "the person record will be soft-deleted. credits and agreements will remain linked.",
|
|
682
|
+
display_name: displayName,
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
case "pica_agreements_delete": {
|
|
686
|
+
const agreementResult = await pica.agreements
|
|
687
|
+
.get(args.id)
|
|
688
|
+
.catch(() => null);
|
|
689
|
+
const title = agreementResult?.agreement?.title || args.id;
|
|
690
|
+
return {
|
|
691
|
+
action: "delete agreement",
|
|
692
|
+
target: title,
|
|
693
|
+
warning: "this will remove all linked work and party records. this cannot be undone.",
|
|
694
|
+
display_name: displayName,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
case "pica_merge_duplicates": {
|
|
698
|
+
const loserCount = args.loser_ids?.length || 0;
|
|
699
|
+
let entityType = "entities";
|
|
700
|
+
try {
|
|
701
|
+
entityType = coerceMergeEntityType(args.entity_type).canonical;
|
|
702
|
+
}
|
|
703
|
+
catch {
|
|
704
|
+
// Unknown entity_type — fall back to generic noun in the warning
|
|
705
|
+
// copy. Tool execution itself will surface the validation error.
|
|
706
|
+
}
|
|
707
|
+
return {
|
|
708
|
+
action: `merge ${entityType}`,
|
|
709
|
+
target: `${loserCount} ${entityType} into winner`,
|
|
710
|
+
warning: `all credits, recordings, and agreements from ${loserCount} loser(s) will be moved to the winner. loser records will be permanently deleted. this cannot be undone.`,
|
|
711
|
+
display_name: displayName,
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
case "pica_projects_delete": {
|
|
715
|
+
const project = await pica.projects?.get(args.id).catch(() => null);
|
|
716
|
+
const projectName = project?.name || args.id;
|
|
717
|
+
return {
|
|
718
|
+
action: "delete project",
|
|
719
|
+
target: projectName,
|
|
720
|
+
warning: "linked works will be unlinked but not deleted. this cannot be undone.",
|
|
721
|
+
display_name: displayName,
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
case "pica_team_remove": {
|
|
725
|
+
return {
|
|
726
|
+
action: "remove team member",
|
|
727
|
+
target: args.id,
|
|
728
|
+
warning: "their user account will be permanently deleted. works and agreements they contributed will remain. this cannot be undone.",
|
|
729
|
+
display_name: displayName,
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
// ── ADR-199 Stream 5b — 7 fall-through cases now specific ──
|
|
733
|
+
case "pica_recordings_delete": {
|
|
734
|
+
const recording = await pica.recordings
|
|
735
|
+
?.get?.(args.id)
|
|
736
|
+
.catch(() => null);
|
|
737
|
+
const title = recording?.title || args.id;
|
|
738
|
+
const isrc = recording?.isrc;
|
|
739
|
+
return {
|
|
740
|
+
action: "delete recording",
|
|
741
|
+
target: isrc ? `${title} (${isrc})` : title,
|
|
742
|
+
warning: "the recording will be removed from its work and any release. credits attached to this recording will be detached. this cannot be undone.",
|
|
743
|
+
display_name: displayName,
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
case "pica_share_links_delete": {
|
|
747
|
+
const link = await pica.shareLinks.get(args.id).catch(() => null);
|
|
748
|
+
const linkData = link?.data || link;
|
|
749
|
+
const linkName = linkData?.name || args.id;
|
|
750
|
+
return {
|
|
751
|
+
action: "delete share link",
|
|
752
|
+
target: linkName,
|
|
753
|
+
warning: "the link will become inaccessible immediately. recipients with the URL will see a 'no longer available' page. view / download history is removed. to keep the audit trail, prefer pica_share_links_update with is_active=false.",
|
|
754
|
+
display_name: displayName,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
case "pica_agreement_types_delete": {
|
|
758
|
+
return {
|
|
759
|
+
action: "delete agreement type",
|
|
760
|
+
target: args.id,
|
|
761
|
+
warning: "the agreement type will be removed from the catalog. existing agreements that reference this type will keep their type string but lose the template definition. this cannot be undone.",
|
|
762
|
+
display_name: displayName,
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
case "pica_notes_delete": {
|
|
766
|
+
return {
|
|
767
|
+
action: "delete note",
|
|
768
|
+
target: args.id,
|
|
769
|
+
warning: "the note will be permanently removed. attachments referenced by the note are not deleted. this cannot be undone.",
|
|
770
|
+
display_name: displayName,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
case "pica_physical_assets_delete": {
|
|
774
|
+
const asset = await pica.physicalAssets
|
|
775
|
+
?.get?.(args.id)
|
|
776
|
+
.catch(() => null);
|
|
777
|
+
const assetName = asset?.name || asset?.title || args.id;
|
|
778
|
+
return {
|
|
779
|
+
action: "delete physical asset",
|
|
780
|
+
target: assetName,
|
|
781
|
+
warning: "the asset record and any linked recording / work associations will be removed. this cannot be undone — re-create from scratch if you need it back.",
|
|
782
|
+
display_name: displayName,
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
case "pica_memory_delete": {
|
|
786
|
+
return {
|
|
787
|
+
action: "delete memory entry",
|
|
788
|
+
target: args.id,
|
|
789
|
+
warning: "the memory entry will be permanently removed and will not appear in future agent context. this cannot be undone.",
|
|
790
|
+
display_name: displayName,
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
case "pica_delete_my_account": {
|
|
794
|
+
return {
|
|
795
|
+
action: "delete account",
|
|
796
|
+
target: "your account and organisation",
|
|
797
|
+
warning: "this is permanent. your auth user, organisation membership, API keys, OAuth tokens, agent grants, and notification preferences will all be removed. catalog data (works, recordings, credits) is retained per data-retention policy. this cannot be undone.",
|
|
798
|
+
display_name: displayName,
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
case "pica_revoke_agent_grant": {
|
|
802
|
+
const grantId = args.grant_id || args.id;
|
|
803
|
+
return {
|
|
804
|
+
action: "revoke agent grant",
|
|
805
|
+
target: grantId,
|
|
806
|
+
warning: "the grant token will stop authenticating immediately. any in-flight agent calls using this grant will fail at next request. this cannot be undone — issue a new grant if needed.",
|
|
807
|
+
display_name: displayName,
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
default: {
|
|
811
|
+
return {
|
|
812
|
+
action: displayName,
|
|
813
|
+
target: args.id || "unknown",
|
|
814
|
+
warning: "this cannot be undone.",
|
|
815
|
+
display_name: displayName,
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
catch {
|
|
821
|
+
return {
|
|
822
|
+
action: displayName,
|
|
823
|
+
target: args.id || "unknown",
|
|
824
|
+
warning: "this cannot be undone.",
|
|
825
|
+
display_name: displayName,
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Sanitize tool parameters for audit logging.
|
|
831
|
+
* Strips confirmation tokens and truncates large string values.
|
|
832
|
+
*/
|
|
833
|
+
sanitizeParams(args) {
|
|
834
|
+
const sanitized = { ...args };
|
|
835
|
+
delete sanitized.confirmation_token;
|
|
836
|
+
for (const [key, value] of Object.entries(sanitized)) {
|
|
837
|
+
if (typeof value === "string" && value.length > 500) {
|
|
838
|
+
sanitized[key] = value.substring(0, 500) + "...[truncated]";
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return sanitized;
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* ADR-208 Primitive B — opaque per-org cache key.
|
|
845
|
+
*
|
|
846
|
+
* Stdio: `config.picaApiKey` is the org-bound API key from credentials,
|
|
847
|
+
* unique per org. HTTP non-discovery: `callerContext.callerIdentity` is
|
|
848
|
+
* `http:${authContext.keyId}` set by `app/api/mcp/route.ts`, also unique
|
|
849
|
+
* per org. This is the cross-org cache isolation invariant required by
|
|
850
|
+
* acceptance criterion 4 — two orgs sharing a worker process never share
|
|
851
|
+
* a cacheKey. Returns `null` (and the wrapper skips session_state) when
|
|
852
|
+
* neither identifier is present.
|
|
853
|
+
*/
|
|
854
|
+
getSessionCacheKey() {
|
|
855
|
+
const apiKey = this.config?.picaApiKey;
|
|
856
|
+
if (apiKey)
|
|
857
|
+
return apiKey;
|
|
858
|
+
if (this.callerContext.callerIdentity &&
|
|
859
|
+
this.callerContext.callerIdentity !== "unknown") {
|
|
860
|
+
return this.callerContext.callerIdentity;
|
|
861
|
+
}
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* ADR-208 Primitive B + ADR-208 Phase 2 + ADR-210 Phase 1 — fetch the
|
|
866
|
+
* org-scoped counts + billing slice via `pica.catalogStats()`. The
|
|
867
|
+
* endpoint composes the billing slice server-side via
|
|
868
|
+
* `subscriptionService.getOrgBillingSlice` and the pending-uploads count
|
|
869
|
+
* via the new `organisations.pending_uploads_count` column so stdio +
|
|
870
|
+
* HTTP transports stay symmetric.
|
|
871
|
+
*
|
|
872
|
+
* `completeness_pct` is stubbed at 0 in Phase 1 and will be wired in a
|
|
873
|
+
* Phase 1.5 follow-up. The stub keeps the shape contract green without
|
|
874
|
+
* forcing a separate endpoint extension into this PR.
|
|
875
|
+
*
|
|
876
|
+
* Fail-soft: when `this.pica` is missing (HTTP-discovery without bearer
|
|
877
|
+
* scope, mostly), every count is 0 and the billing slice falls back to
|
|
878
|
+
* a conservative "trial" shape so the agent doesn't see misleading
|
|
879
|
+
* "active" billing for an uninitialized context.
|
|
880
|
+
*/
|
|
881
|
+
fetchSessionCounts = async () => {
|
|
882
|
+
if (!this.pica) {
|
|
883
|
+
return {
|
|
884
|
+
works_count: 0,
|
|
885
|
+
recordings_count: 0,
|
|
886
|
+
completeness_pct: 0,
|
|
887
|
+
pending_uploads: 0,
|
|
888
|
+
billing_state: "trial",
|
|
889
|
+
trial_days_remaining: null,
|
|
890
|
+
current_tier: null,
|
|
891
|
+
capacity_pct: 0,
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
const stats = await this.pica.catalogStats();
|
|
895
|
+
const billing = stats.billing_slice;
|
|
896
|
+
// ADR-217 Primitive D — storage slice from /api/admin/catalog/stats.
|
|
897
|
+
// Optional in the response so older servers without ADR-217 keep
|
|
898
|
+
// working — when missing, omit the key entirely (vs fabricating a
|
|
899
|
+
// misleading "pica_s3 / untested" envelope that would suppress an
|
|
900
|
+
// agent's "you might want to configure storage" nudge).
|
|
901
|
+
const storage = stats.storage_slice;
|
|
902
|
+
const counts = {
|
|
903
|
+
works_count: stats.works?.total ?? 0,
|
|
904
|
+
recordings_count: stats.recordings?.total ?? 0,
|
|
905
|
+
// Phase 1.5 stub — completeness_pct ships separately. Shape preserved.
|
|
906
|
+
completeness_pct: 0,
|
|
907
|
+
pending_uploads: stats.pending_uploads_count ?? 0,
|
|
908
|
+
// ADR-210 Phase 1 — billing slice from /api/admin/catalog/stats. The
|
|
909
|
+
// endpoint emits a "trial" fallback on its own billing-side outage; if
|
|
910
|
+
// the field is missing entirely (older server pre-Phase-1), mirror the
|
|
911
|
+
// same conservative shape locally rather than fabricate "active".
|
|
912
|
+
billing_state: billing?.billing_state ?? "trial",
|
|
913
|
+
trial_days_remaining: billing?.trial_days_remaining ?? null,
|
|
914
|
+
current_tier: billing?.current_tier ?? null,
|
|
915
|
+
capacity_pct: billing?.capacity_pct ?? 0,
|
|
916
|
+
...(storage
|
|
917
|
+
? {
|
|
918
|
+
storage_provider: {
|
|
919
|
+
provider: storage.provider,
|
|
920
|
+
is_byoc: storage.is_byoc,
|
|
921
|
+
last_test_status: storage.last_test_status,
|
|
922
|
+
},
|
|
923
|
+
}
|
|
924
|
+
: {}),
|
|
925
|
+
};
|
|
926
|
+
// ADR-218 Primitive A — propagate the optional notifications summary
|
|
927
|
+
// when the catalog-stats response carries it. Older API servers
|
|
928
|
+
// (pre-ADR-218) omit the field; we leave the envelope key off in that
|
|
929
|
+
// case so the agent doesn't see a fabricated zero.
|
|
930
|
+
if (stats.notifications) {
|
|
931
|
+
counts.notifications = stats.notifications;
|
|
932
|
+
}
|
|
933
|
+
return counts;
|
|
934
|
+
};
|
|
935
|
+
/**
|
|
936
|
+
* P3c — attach `_meta.schemas` (workflow-resource URIs) to a successful
|
|
937
|
+
* workflow-tagged tool result. Mirrors `attachSessionState`'s merge
|
|
938
|
+
* semantics: spread existing `_meta`, add the new key, leave other
|
|
939
|
+
* keys (e.g. `session_state`) untouched.
|
|
940
|
+
*
|
|
941
|
+
* Applies to ALL workflow-tagged tools (Option X), including read
|
|
942
|
+
* companions like `pica_sessions_list`, so the agent gets the schema
|
|
943
|
+
* pointer no matter which verb they hit first. Skips when:
|
|
944
|
+
* - the tool's `workflows` is or contains only `"infrastructure"`
|
|
945
|
+
* (no schema resource exists), or
|
|
946
|
+
* - the result is an error (the workflow didn't progress).
|
|
947
|
+
*
|
|
948
|
+
* Fail-open like `attachSessionState`: derivation is pure today, but
|
|
949
|
+
* any future throw must not turn a successful tool result into a
|
|
950
|
+
* failure.
|
|
951
|
+
*/
|
|
952
|
+
attachSchemaHints(result, workflows) {
|
|
953
|
+
try {
|
|
954
|
+
const tags = Array.isArray(workflows) ? workflows : [workflows];
|
|
955
|
+
const schemas = tags
|
|
956
|
+
.filter((t) => t !== "infrastructure")
|
|
957
|
+
.map((t) => `pica://schemas/${t}`);
|
|
958
|
+
if (schemas.length === 0)
|
|
959
|
+
return;
|
|
960
|
+
const existingMeta = result._meta ?? {};
|
|
961
|
+
result._meta = {
|
|
962
|
+
...existingMeta,
|
|
963
|
+
schemas,
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
catch {
|
|
967
|
+
// Match attachSessionState shape — fail-open. The hint is a
|
|
968
|
+
// discoverability aid, never load-bearing for the tool's own work.
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* ADR-208 Primitive B — bump the session-mutation counters and attach
|
|
973
|
+
* the resolved `_meta.session_state` to a successful write-tool result.
|
|
974
|
+
* Mutates `result` in-place to keep the existing return contract.
|
|
975
|
+
* Fail-open: any error here is swallowed — ambient state is best-effort
|
|
976
|
+
* and must not turn a successful mutation into a tool failure.
|
|
977
|
+
*/
|
|
978
|
+
async attachSessionState(result, toolName) {
|
|
979
|
+
const cacheKey = this.getSessionCacheKey();
|
|
980
|
+
if (!cacheKey)
|
|
981
|
+
return;
|
|
982
|
+
try {
|
|
983
|
+
incrementSessionMutations(cacheKey);
|
|
984
|
+
const sessionState = await buildSessionState(cacheKey, this.fetchSessionCounts);
|
|
985
|
+
if (!sessionState)
|
|
986
|
+
return;
|
|
987
|
+
const existingMeta = result._meta ?? {};
|
|
988
|
+
result._meta = {
|
|
989
|
+
...existingMeta,
|
|
990
|
+
session_state: sessionState,
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
catch {
|
|
994
|
+
// Swallow — ADR-208 explicitly mandates fail-open. The counter has
|
|
995
|
+
// already advanced; not attaching session_state on this turn just
|
|
996
|
+
// means the agent gets the bumped counters on the next mutation.
|
|
997
|
+
void toolName;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Execute a tool by name. `ctx.server` carries the transport-scoped
|
|
1002
|
+
* MCP `Server` reference so executors that need `server.elicitInput()`
|
|
1003
|
+
* (ADR-200 Phase 1) can reach it. Each per-request HTTP server is
|
|
1004
|
+
* different; passing it through positionally avoids registry-level
|
|
1005
|
+
* mutable state that would race between concurrent requests.
|
|
1006
|
+
*/
|
|
1007
|
+
async executeTool(name, args, ctx) {
|
|
1008
|
+
const startTime = Date.now();
|
|
1009
|
+
const tool = this.tools.get(name);
|
|
1010
|
+
if (!tool) {
|
|
1011
|
+
throw new ToolExecutionError(`Tool not found: ${name}`);
|
|
1012
|
+
}
|
|
1013
|
+
const metadata = getToolMetadata(name);
|
|
1014
|
+
// ── Session freshness gate (ADR-151) ──
|
|
1015
|
+
if (metadata?.risk === "destructive" &&
|
|
1016
|
+
this.config &&
|
|
1017
|
+
!this.config.lobbyMode) {
|
|
1018
|
+
const creds = readCredentials(this.config.credentialsPath);
|
|
1019
|
+
if (creds?.authenticated_at) {
|
|
1020
|
+
const authAge = Date.now() - new Date(creds.authenticated_at).getTime();
|
|
1021
|
+
const sevenDays = 7 * 24 * 60 * 60 * 1000;
|
|
1022
|
+
if (authAge > sevenDays) {
|
|
1023
|
+
return {
|
|
1024
|
+
content: [
|
|
1025
|
+
{
|
|
1026
|
+
type: "text",
|
|
1027
|
+
text: 'this action requires re-verification — say "sign me in" to confirm your identity',
|
|
1028
|
+
},
|
|
1029
|
+
],
|
|
1030
|
+
isError: true,
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
// ── Destructive confirmation gate ──
|
|
1036
|
+
if (metadata?.risk === "destructive") {
|
|
1037
|
+
const confirmationToken = args.confirmation_token;
|
|
1038
|
+
if (!confirmationToken) {
|
|
1039
|
+
// First call — generate preview and return confirmation challenge
|
|
1040
|
+
const token = generateConfirmationToken(name, args);
|
|
1041
|
+
const preview = await this.buildDestructivePreview(name, args);
|
|
1042
|
+
const response = {
|
|
1043
|
+
content: [
|
|
1044
|
+
{
|
|
1045
|
+
type: "text",
|
|
1046
|
+
text: JSON.stringify({
|
|
1047
|
+
status: "confirmation_required",
|
|
1048
|
+
confirmation_token: token,
|
|
1049
|
+
expires_in: 120,
|
|
1050
|
+
preview,
|
|
1051
|
+
}, null, 2),
|
|
1052
|
+
},
|
|
1053
|
+
],
|
|
1054
|
+
structuredContent: {
|
|
1055
|
+
status: "confirmation_required",
|
|
1056
|
+
confirmation_token: token,
|
|
1057
|
+
expires_in: 120,
|
|
1058
|
+
preview,
|
|
1059
|
+
},
|
|
1060
|
+
};
|
|
1061
|
+
// Fire-and-forget audit for confirmation challenge
|
|
1062
|
+
this.auditLogger
|
|
1063
|
+
?.logToolExecution({
|
|
1064
|
+
tool_name: name,
|
|
1065
|
+
tool_category: metadata?.category || "unknown",
|
|
1066
|
+
risk_level: "destructive",
|
|
1067
|
+
parameters: this.sanitizeParams(args),
|
|
1068
|
+
result_status: "confirmation_required",
|
|
1069
|
+
confirmation_token: token,
|
|
1070
|
+
execution_time_ms: Date.now() - startTime,
|
|
1071
|
+
caller_identity: this.callerContext.callerIdentity,
|
|
1072
|
+
transport: this.callerContext.transport,
|
|
1073
|
+
})
|
|
1074
|
+
.catch(() => { }); // Never block on audit
|
|
1075
|
+
return response;
|
|
1076
|
+
}
|
|
1077
|
+
// Second call — validate token
|
|
1078
|
+
const validation = validateAndConsumeToken(confirmationToken, name, args);
|
|
1079
|
+
if (!validation.valid) {
|
|
1080
|
+
return {
|
|
1081
|
+
content: [
|
|
1082
|
+
{
|
|
1083
|
+
type: "text",
|
|
1084
|
+
text: JSON.stringify({
|
|
1085
|
+
error: validation.reason?.includes("expired")
|
|
1086
|
+
? "CONFIRMATION_EXPIRED"
|
|
1087
|
+
: "CONFIRMATION_INVALID",
|
|
1088
|
+
message: validation.reason,
|
|
1089
|
+
retry_safe: false,
|
|
1090
|
+
suggestion: "start the operation again without the confirmation token",
|
|
1091
|
+
}, null, 2),
|
|
1092
|
+
},
|
|
1093
|
+
],
|
|
1094
|
+
isError: true,
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
// Token valid — fall through to execute
|
|
1098
|
+
}
|
|
1099
|
+
// ── Normal execution ──
|
|
1100
|
+
try {
|
|
1101
|
+
const result = await tool.executor(args, ctx);
|
|
1102
|
+
// Guard response size — truncate oversized payloads with recovery hint
|
|
1103
|
+
if (result?.content) {
|
|
1104
|
+
for (const block of result.content) {
|
|
1105
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
1106
|
+
block.text = guardResponseSize(block.text, name);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
// ADR-208 Primitive B — ambient session-state on write-tool results.
|
|
1111
|
+
// Read tools (`risk: safe` ⇒ `readOnlyHint: true`) skip this entirely so
|
|
1112
|
+
// the high-volume read path stays cheap. `result.isError === true` also
|
|
1113
|
+
// skips because a failed mutation didn't change catalog state.
|
|
1114
|
+
const isWriteTool = (metadata?.risk ?? this.classifyTool(name)) !== "safe";
|
|
1115
|
+
if (isWriteTool && result && !result.isError) {
|
|
1116
|
+
await this.attachSessionState(result, name);
|
|
1117
|
+
}
|
|
1118
|
+
// P3c — schema-resource hint on every workflow-tagged tool result
|
|
1119
|
+
// (Option X — applies to read companions too so the agent gets the
|
|
1120
|
+
// pointer regardless of which verb they hit first). Runs after
|
|
1121
|
+
// `attachSessionState` so the two `_meta` keys co-exist via merge.
|
|
1122
|
+
// Skipped on errors (workflow didn't progress) and on infrastructure
|
|
1123
|
+
// tools (handled inside `attachSchemaHints`).
|
|
1124
|
+
if (result && !result.isError) {
|
|
1125
|
+
this.attachSchemaHints(result, tool.definition.workflows);
|
|
1126
|
+
}
|
|
1127
|
+
// ADR-208 acceptance criterion 5 — briefing run resets the
|
|
1128
|
+
// since_last_briefing counter. Handled in the wrapper so the briefing
|
|
1129
|
+
// tool itself stays oblivious to the cacheKey it doesn't own.
|
|
1130
|
+
if (name === "pica_dashboard_briefing" && result && !result.isError) {
|
|
1131
|
+
const cacheKey = this.getSessionCacheKey();
|
|
1132
|
+
if (cacheKey)
|
|
1133
|
+
resetSinceLastBriefing(cacheKey);
|
|
1134
|
+
}
|
|
1135
|
+
// Fire-and-forget audit
|
|
1136
|
+
this.auditLogger
|
|
1137
|
+
?.logToolExecution({
|
|
1138
|
+
tool_name: name,
|
|
1139
|
+
tool_category: metadata?.category || "unknown",
|
|
1140
|
+
risk_level: metadata?.risk || this.classifyTool(name),
|
|
1141
|
+
parameters: this.sanitizeParams(args),
|
|
1142
|
+
result_status: "success",
|
|
1143
|
+
execution_time_ms: Date.now() - startTime,
|
|
1144
|
+
caller_identity: this.callerContext.callerIdentity,
|
|
1145
|
+
transport: this.callerContext.transport,
|
|
1146
|
+
})
|
|
1147
|
+
.catch(() => { }); // Never block on audit
|
|
1148
|
+
return result;
|
|
1149
|
+
}
|
|
1150
|
+
catch (error) {
|
|
1151
|
+
const formatted = formatError(error);
|
|
1152
|
+
const parsed = JSON.parse(formatted.text);
|
|
1153
|
+
// Attach recovery hint if available for this tool + error code
|
|
1154
|
+
const hint = getRecoveryHint(name, parsed.error);
|
|
1155
|
+
if (hint) {
|
|
1156
|
+
parsed.suggestion = hint.suggestion;
|
|
1157
|
+
if (hint.next_tool) {
|
|
1158
|
+
parsed.next_tool = hint.next_tool;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
// Fire-and-forget audit for errors
|
|
1162
|
+
this.auditLogger
|
|
1163
|
+
?.logToolExecution({
|
|
1164
|
+
tool_name: name,
|
|
1165
|
+
tool_category: metadata?.category || "unknown",
|
|
1166
|
+
risk_level: metadata?.risk || this.classifyTool(name),
|
|
1167
|
+
parameters: this.sanitizeParams(args),
|
|
1168
|
+
result_status: "error",
|
|
1169
|
+
error_code: parsed.error,
|
|
1170
|
+
execution_time_ms: Date.now() - startTime,
|
|
1171
|
+
caller_identity: this.callerContext.callerIdentity,
|
|
1172
|
+
transport: this.callerContext.transport,
|
|
1173
|
+
})
|
|
1174
|
+
.catch(() => { }); // Never block on audit
|
|
1175
|
+
return {
|
|
1176
|
+
content: [{ type: "text", text: JSON.stringify(parsed, null, 2) }],
|
|
1177
|
+
isError: true,
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
//# sourceMappingURL=index.js.map
|