@withpica/mcp-server 2.7.0 → 2.9.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/dist/__mocks__/mppx-mcp-sdk-server.d.ts +6 -0
- package/dist/__mocks__/mppx-mcp-sdk-server.d.ts.map +1 -0
- package/dist/__mocks__/mppx-mcp-sdk-server.js +6 -0
- package/dist/__mocks__/mppx-mcp-sdk-server.js.map +1 -0
- package/dist/__mocks__/mppx-server.d.ts +12 -0
- package/dist/__mocks__/mppx-server.d.ts.map +1 -0
- package/dist/__mocks__/mppx-server.js +12 -0
- package/dist/__mocks__/mppx-server.js.map +1 -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/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 +9 -30
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +36 -12
- package/dist/config.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/prompts/index.js +24 -24
- package/dist/prompts/index.js.map +1 -1
- package/dist/resources/index.d.ts +4 -2
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +133 -54
- package/dist/resources/index.js.map +1 -1
- 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/server-instructions.d.ts +9 -0
- package/dist/server-instructions.d.ts.map +1 -0
- package/dist/server-instructions.js +34 -0
- package/dist/server-instructions.js.map +1 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +103 -14
- package/dist/server.js.map +1 -1
- 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 +387 -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 +363 -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 +124 -0
- package/dist/tools/analytics.js.map +1 -0
- package/dist/tools/app-tools.d.ts +21 -0
- package/dist/tools/app-tools.d.ts.map +1 -0
- package/dist/tools/app-tools.js +248 -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 +631 -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 +384 -0
- package/dist/tools/audio-files.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 +210 -0
- package/dist/tools/auth.js.map +1 -0
- package/dist/tools/bulk.d.ts +16 -0
- package/dist/tools/bulk.d.ts.map +1 -0
- package/dist/tools/bulk.js +85 -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 +67 -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 +311 -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 +78 -0
- package/dist/tools/comparisons.js.map +1 -0
- package/dist/tools/credits.d.ts +17 -0
- package/dist/tools/credits.d.ts.map +1 -0
- package/dist/tools/credits.js +312 -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 +274 -0
- package/dist/tools/custody.js.map +1 -0
- package/dist/tools/dashboard.d.ts +19 -0
- package/dist/tools/dashboard.d.ts.map +1 -0
- package/dist/tools/dashboard.js +146 -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 +106 -0
- package/dist/tools/directory.js.map +1 -0
- package/dist/tools/discovery.d.ts +28 -0
- package/dist/tools/discovery.d.ts.map +1 -0
- package/dist/tools/discovery.js +560 -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 +61 -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 +36 -0
- package/dist/tools/documents.js.map +1 -0
- package/dist/tools/duplicates.d.ts +16 -0
- package/dist/tools/duplicates.d.ts.map +1 -0
- package/dist/tools/duplicates.js +87 -0
- package/dist/tools/duplicates.js.map +1 -0
- package/dist/tools/enrichment.d.ts +23 -0
- package/dist/tools/enrichment.d.ts.map +1 -0
- package/dist/tools/enrichment.js +220 -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 +175 -0
- package/dist/tools/exports.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 +203 -0
- package/dist/tools/import-documents.js.map +1 -0
- package/dist/tools/import.d.ts +30 -0
- package/dist/tools/import.d.ts.map +1 -0
- package/dist/tools/import.js +455 -0
- package/dist/tools/import.js.map +1 -0
- package/dist/tools/index.d.ts +67 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +448 -84
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/integrations.d.ts +15 -0
- package/dist/tools/integrations.d.ts.map +1 -0
- package/dist/tools/integrations.js +100 -0
- package/dist/tools/integrations.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 +431 -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 +116 -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 +1106 -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 +291 -0
- package/dist/tools/multimedia.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 +108 -0
- package/dist/tools/notes.js.map +1 -0
- package/dist/tools/notifications.d.ts +20 -0
- package/dist/tools/notifications.d.ts.map +1 -0
- package/dist/tools/notifications.js +266 -0
- package/dist/tools/notifications.js.map +1 -0
- package/dist/tools/people.d.ts +5 -13
- package/dist/tools/people.d.ts.map +1 -1
- package/dist/tools/people.js +148 -109
- package/dist/tools/people.js.map +1 -1
- package/dist/tools/projects.d.ts +19 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +140 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/publishers.d.ts +16 -0
- package/dist/tools/publishers.d.ts.map +1 -0
- package/dist/tools/publishers.js +69 -0
- package/dist/tools/publishers.js.map +1 -0
- package/dist/tools/purchases.d.ts +15 -0
- package/dist/tools/purchases.d.ts.map +1 -0
- package/dist/tools/purchases.js +63 -0
- package/dist/tools/purchases.js.map +1 -0
- package/dist/tools/recordings.d.ts +35 -35
- package/dist/tools/recordings.d.ts.map +1 -1
- package/dist/tools/recordings.js +136 -48
- package/dist/tools/recordings.js.map +1 -1
- package/dist/tools/recovery-hints.d.ts +14 -0
- package/dist/tools/recovery-hints.d.ts.map +1 -0
- package/dist/tools/recovery-hints.js +277 -0
- package/dist/tools/recovery-hints.js.map +1 -0
- package/dist/tools/releases.d.ts +18 -0
- package/dist/tools/releases.d.ts.map +1 -0
- package/dist/tools/releases.js +128 -0
- package/dist/tools/releases.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 +257 -0
- package/dist/tools/royalties.js.map +1 -0
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +3 -3
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/send.d.ts +17 -0
- package/dist/tools/send.d.ts.map +1 -0
- package/dist/tools/send.js +185 -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 +115 -0
- package/dist/tools/sessions.js.map +1 -0
- package/dist/tools/settings.d.ts +18 -0
- package/dist/tools/settings.d.ts.map +1 -0
- package/dist/tools/settings.js +96 -0
- package/dist/tools/settings.js.map +1 -0
- package/dist/tools/share-links.d.ts +19 -0
- package/dist/tools/share-links.d.ts.map +1 -0
- package/dist/tools/share-links.js +121 -0
- package/dist/tools/share-links.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 +307 -0
- package/dist/tools/split-sheets.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 +134 -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 +141 -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 +156 -0
- package/dist/tools/uploads.js.map +1 -0
- package/dist/tools/works.d.ts +5 -13
- package/dist/tools/works.d.ts.map +1 -1
- package/dist/tools/works.js +219 -116
- package/dist/tools/works.js.map +1 -1
- package/package.json +1 -1
- package/dist/pica-sdk.d.ts +0 -1231
- package/dist/pica-sdk.d.ts.map +0 -1
- package/dist/pica-sdk.js +0 -1403
- package/dist/pica-sdk.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -29
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -115
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/formatting.d.ts +0 -82
- package/dist/utils/formatting.d.ts.map +0 -1
- package/dist/utils/formatting.js +0 -125
- package/dist/utils/formatting.js.map +0 -1
package/dist/tools/index.js
CHANGED
|
@@ -5,14 +5,11 @@ import { RecordingsTools } from "./recordings.js";
|
|
|
5
5
|
import { SearchTools } from "./search.js";
|
|
6
6
|
import { LicensingTools } from "./licensing.js";
|
|
7
7
|
import { CreditsTools } from "./credits.js";
|
|
8
|
-
import { PicaScoreTools } from "./pica-score.js";
|
|
9
8
|
import { AudioFilesTools } from "./audio-files.js";
|
|
10
9
|
import { MultimediaTools } from "./multimedia.js";
|
|
11
10
|
import { AgreementsTools } from "./agreements.js";
|
|
12
11
|
import { MemoryTools } from "./memory.js";
|
|
13
12
|
import { EnrichmentTools } from "./enrichment.js";
|
|
14
|
-
import { RegistrationTools } from "./registration.js";
|
|
15
|
-
import { HealthTools } from "./health.js";
|
|
16
13
|
import { BulkTools } from "./bulk.js";
|
|
17
14
|
import { ExportTools } from "./exports.js";
|
|
18
15
|
import { DuplicatesTools } from "./duplicates.js";
|
|
@@ -41,241 +38,308 @@ import { SettingsTools } from "./settings.js";
|
|
|
41
38
|
import { RoyaltiesTools } from "./royalties.js";
|
|
42
39
|
import { ShareLinksTools } from "./share-links.js";
|
|
43
40
|
import { DisputesTools } from "./disputes.js";
|
|
41
|
+
import { CustodyTools } from "./custody.js";
|
|
44
42
|
import { PurchasesTools } from "./purchases.js";
|
|
45
43
|
import { TelegramTools } from "./telegram.js";
|
|
46
|
-
import {
|
|
44
|
+
import { PublishersTools } from "./publishers.js";
|
|
45
|
+
import { AuthTools } from "./auth.js";
|
|
46
|
+
import { AppTools } from "./app-tools.js";
|
|
47
|
+
import { DiscoveryTools } from "./discovery.js";
|
|
48
|
+
import { readCredentials } from "@withpica/mcp-utils";
|
|
49
|
+
import { formatError, ToolExecutionError } from "@withpica/mcp-utils";
|
|
50
|
+
import { guardResponseSize } from "@withpica/mcp-utils";
|
|
51
|
+
import { getToolMetadata } from "./metadata.js";
|
|
52
|
+
import { getRecoveryHint } from "./recovery-hints.js";
|
|
53
|
+
import { generateConfirmationToken, validateAndConsumeToken, } from "@withpica/mcp-utils";
|
|
54
|
+
/**
|
|
55
|
+
* Build a tool description with metadata injected.
|
|
56
|
+
* Format: "[category] original description"
|
|
57
|
+
*/
|
|
58
|
+
export function injectMetadataIntoDescription(description, metadata) {
|
|
59
|
+
// Category tag helps the agent understand tool grouping.
|
|
60
|
+
// Risk prefixes removed — annotations (readOnlyHint, destructiveHint) handle this structurally.
|
|
61
|
+
// Behavioral guidance (tell user what you're doing) is in server instructions (one copy).
|
|
62
|
+
return `[${metadata.category}] ${description}`;
|
|
63
|
+
}
|
|
64
|
+
export const CATEGORY_DISPLAY = {
|
|
65
|
+
catalog: "manage your catalog",
|
|
66
|
+
enrichment: "enrich your metadata",
|
|
67
|
+
business: "handle business and agreements",
|
|
68
|
+
discovery: "search and explore",
|
|
69
|
+
media: "manage photos, audio, and video",
|
|
70
|
+
comms: "send and share",
|
|
71
|
+
settings: "manage your account and team",
|
|
72
|
+
};
|
|
47
73
|
export class ToolRegistry {
|
|
48
74
|
tools;
|
|
49
75
|
pica;
|
|
50
|
-
|
|
76
|
+
config;
|
|
77
|
+
reinitializeCallback;
|
|
78
|
+
signOutCallback;
|
|
79
|
+
auditLogger;
|
|
80
|
+
callerContext;
|
|
81
|
+
constructor(pica, config, reinitializeCallback, callerContext, signOutCallback) {
|
|
51
82
|
this.pica = pica;
|
|
83
|
+
this.config = config;
|
|
84
|
+
this.reinitializeCallback = reinitializeCallback;
|
|
85
|
+
this.signOutCallback = signOutCallback;
|
|
86
|
+
this.callerContext = callerContext ?? {
|
|
87
|
+
callerIdentity: "unknown",
|
|
88
|
+
transport: "stdio",
|
|
89
|
+
};
|
|
52
90
|
this.tools = new Map();
|
|
53
91
|
this.registerAllTools();
|
|
54
92
|
}
|
|
93
|
+
setAuditLogger(logger) {
|
|
94
|
+
this.auditLogger = logger;
|
|
95
|
+
}
|
|
96
|
+
setCallerContext(context) {
|
|
97
|
+
this.callerContext = context;
|
|
98
|
+
}
|
|
55
99
|
/**
|
|
56
100
|
* Register all available tools
|
|
57
101
|
*/
|
|
58
102
|
registerAllTools() {
|
|
103
|
+
// Auth tools — always registered (lobby + authenticated mode)
|
|
104
|
+
if (this.config) {
|
|
105
|
+
const authTools = new AuthTools(this.config, this.reinitializeCallback || (() => { }), this.signOutCallback || (() => { }));
|
|
106
|
+
authTools.getTools().forEach((tool) => {
|
|
107
|
+
this.tools.set(tool.definition.name, tool);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// In lobby mode, only auth tools are available
|
|
111
|
+
if (this.config?.lobbyMode || !this.pica) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const pica = this.pica;
|
|
59
115
|
// Works tools
|
|
60
|
-
const worksTools = new WorksTools(
|
|
116
|
+
const worksTools = new WorksTools(pica);
|
|
61
117
|
worksTools.getTools().forEach((tool) => {
|
|
62
118
|
this.tools.set(tool.definition.name, tool);
|
|
63
119
|
});
|
|
64
120
|
// People tools
|
|
65
|
-
const peopleTools = new PeopleTools(
|
|
121
|
+
const peopleTools = new PeopleTools(pica);
|
|
66
122
|
peopleTools.getTools().forEach((tool) => {
|
|
67
123
|
this.tools.set(tool.definition.name, tool);
|
|
68
124
|
});
|
|
69
125
|
// Recordings tools
|
|
70
|
-
const recordingsTools = new RecordingsTools(
|
|
126
|
+
const recordingsTools = new RecordingsTools(pica);
|
|
71
127
|
recordingsTools.getTools().forEach((tool) => {
|
|
72
128
|
this.tools.set(tool.definition.name, tool);
|
|
73
129
|
});
|
|
74
130
|
// Search tools
|
|
75
|
-
const searchTools = new SearchTools(
|
|
131
|
+
const searchTools = new SearchTools(pica);
|
|
76
132
|
searchTools.getTools().forEach((tool) => {
|
|
77
133
|
this.tools.set(tool.definition.name, tool);
|
|
78
134
|
});
|
|
79
135
|
// Licensing tools
|
|
80
|
-
const licensingTools = new LicensingTools(
|
|
136
|
+
const licensingTools = new LicensingTools(pica);
|
|
81
137
|
licensingTools.getTools().forEach((tool) => {
|
|
82
138
|
this.tools.set(tool.definition.name, tool);
|
|
83
139
|
});
|
|
84
140
|
// Credits tools
|
|
85
|
-
const creditsTools = new CreditsTools(
|
|
141
|
+
const creditsTools = new CreditsTools(pica);
|
|
86
142
|
creditsTools.getTools().forEach((tool) => {
|
|
87
143
|
this.tools.set(tool.definition.name, tool);
|
|
88
144
|
});
|
|
89
|
-
// PICA Score tools
|
|
90
|
-
const picaScoreTools = new PicaScoreTools(this.pica);
|
|
91
|
-
picaScoreTools.getTools().forEach((tool) => {
|
|
92
|
-
this.tools.set(tool.definition.name, tool);
|
|
93
|
-
});
|
|
94
145
|
// Audio Files tools
|
|
95
|
-
const audioFilesTools = new AudioFilesTools(
|
|
146
|
+
const audioFilesTools = new AudioFilesTools(pica);
|
|
96
147
|
audioFilesTools.getTools().forEach((tool) => {
|
|
97
148
|
this.tools.set(tool.definition.name, tool);
|
|
98
149
|
});
|
|
99
150
|
// Multimedia tools
|
|
100
|
-
const multimediaTools = new MultimediaTools(
|
|
151
|
+
const multimediaTools = new MultimediaTools(pica);
|
|
101
152
|
multimediaTools.getTools().forEach((tool) => {
|
|
102
153
|
this.tools.set(tool.definition.name, tool);
|
|
103
154
|
});
|
|
104
155
|
// Agreements tools
|
|
105
|
-
const agreementsTools = new AgreementsTools(
|
|
156
|
+
const agreementsTools = new AgreementsTools(pica);
|
|
106
157
|
agreementsTools.getTools().forEach((tool) => {
|
|
107
158
|
this.tools.set(tool.definition.name, tool);
|
|
108
159
|
});
|
|
109
160
|
// Memory tools
|
|
110
|
-
const memoryTools = new MemoryTools(
|
|
161
|
+
const memoryTools = new MemoryTools(pica);
|
|
111
162
|
memoryTools.getTools().forEach((tool) => {
|
|
112
163
|
this.tools.set(tool.definition.name, tool);
|
|
113
164
|
});
|
|
114
165
|
// Enrichment tools
|
|
115
|
-
const enrichmentTools = new EnrichmentTools(
|
|
166
|
+
const enrichmentTools = new EnrichmentTools(pica);
|
|
116
167
|
enrichmentTools.getTools().forEach((tool) => {
|
|
117
168
|
this.tools.set(tool.definition.name, tool);
|
|
118
169
|
});
|
|
119
|
-
// Registration tools
|
|
120
|
-
const registrationTools = new RegistrationTools(this.pica);
|
|
121
|
-
registrationTools.getTools().forEach((tool) => {
|
|
122
|
-
this.tools.set(tool.definition.name, tool);
|
|
123
|
-
});
|
|
124
|
-
// Health tools
|
|
125
|
-
const healthTools = new HealthTools(this.pica);
|
|
126
|
-
healthTools.getTools().forEach((tool) => {
|
|
127
|
-
this.tools.set(tool.definition.name, tool);
|
|
128
|
-
});
|
|
129
170
|
// Bulk operations tools
|
|
130
|
-
const bulkTools = new BulkTools(
|
|
171
|
+
const bulkTools = new BulkTools(pica);
|
|
131
172
|
bulkTools.getTools().forEach((tool) => {
|
|
132
173
|
this.tools.set(tool.definition.name, tool);
|
|
133
174
|
});
|
|
134
175
|
// Export tools
|
|
135
|
-
const exportTools = new ExportTools(
|
|
176
|
+
const exportTools = new ExportTools(pica);
|
|
136
177
|
exportTools.getTools().forEach((tool) => {
|
|
137
178
|
this.tools.set(tool.definition.name, tool);
|
|
138
179
|
});
|
|
139
180
|
// Duplicates tools
|
|
140
|
-
const duplicatesTools = new DuplicatesTools(
|
|
181
|
+
const duplicatesTools = new DuplicatesTools(pica);
|
|
141
182
|
duplicatesTools.getTools().forEach((tool) => {
|
|
142
183
|
this.tools.set(tool.definition.name, tool);
|
|
143
184
|
});
|
|
144
185
|
// Directory tools
|
|
145
|
-
const directoryTools = new DirectoryTools(
|
|
186
|
+
const directoryTools = new DirectoryTools(pica);
|
|
146
187
|
directoryTools.getTools().forEach((tool) => {
|
|
147
188
|
this.tools.set(tool.definition.name, tool);
|
|
148
189
|
});
|
|
149
190
|
// Collaborator tools
|
|
150
|
-
const collaboratorsTools = new CollaboratorsTools(
|
|
191
|
+
const collaboratorsTools = new CollaboratorsTools(pica);
|
|
151
192
|
collaboratorsTools.getTools().forEach((tool) => {
|
|
152
193
|
this.tools.set(tool.definition.name, tool);
|
|
153
194
|
});
|
|
154
195
|
// Upload tools (generic file upload — images, videos, documents)
|
|
155
|
-
const uploadTools = new UploadTools(
|
|
196
|
+
const uploadTools = new UploadTools(pica);
|
|
156
197
|
uploadTools.getTools().forEach((tool) => {
|
|
157
198
|
this.tools.set(tool.definition.name, tool);
|
|
158
199
|
});
|
|
159
200
|
// Document tools (AI analysis)
|
|
160
|
-
const documentTools = new DocumentTools(
|
|
201
|
+
const documentTools = new DocumentTools(pica);
|
|
161
202
|
documentTools.getTools().forEach((tool) => {
|
|
162
203
|
this.tools.set(tool.definition.name, tool);
|
|
163
204
|
});
|
|
164
205
|
// Bulk import tools (CSV import pipeline)
|
|
165
|
-
const importTools = new ImportTools(
|
|
206
|
+
const importTools = new ImportTools(pica);
|
|
166
207
|
importTools.getTools().forEach((tool) => {
|
|
167
208
|
this.tools.set(tool.definition.name, tool);
|
|
168
209
|
});
|
|
169
210
|
// Comparison tools (ADR-116 Phase 2: enrichment + registration comparison)
|
|
170
|
-
const comparisonTools = new ComparisonTools(
|
|
211
|
+
const comparisonTools = new ComparisonTools(pica);
|
|
171
212
|
comparisonTools.getTools().forEach((tool) => {
|
|
172
213
|
this.tools.set(tool.definition.name, tool);
|
|
173
214
|
});
|
|
174
215
|
// Send Hub tools (messages, invites, file requests, audio shares)
|
|
175
|
-
const sendTools = new SendTools(
|
|
216
|
+
const sendTools = new SendTools(pica);
|
|
176
217
|
sendTools.getTools().forEach((tool) => {
|
|
177
218
|
this.tools.set(tool.definition.name, tool);
|
|
178
219
|
});
|
|
179
220
|
// Import document tools
|
|
180
|
-
const importDocumentTools = new ImportDocumentTools(
|
|
221
|
+
const importDocumentTools = new ImportDocumentTools(pica);
|
|
181
222
|
importDocumentTools.getTools().forEach((tool) => {
|
|
182
223
|
this.tools.set(tool.definition.name, tool);
|
|
183
224
|
});
|
|
184
225
|
// Physical assets tools (equipment, instruments, studio gear)
|
|
185
|
-
const assetsTools = new AssetsTools(
|
|
226
|
+
const assetsTools = new AssetsTools(pica);
|
|
186
227
|
assetsTools.getTools().forEach((tool) => {
|
|
187
228
|
this.tools.set(tool.definition.name, tool);
|
|
188
229
|
});
|
|
189
230
|
// Work sessions tools (studio session tracking)
|
|
190
|
-
const sessionsTools = new SessionsTools(
|
|
231
|
+
const sessionsTools = new SessionsTools(pica);
|
|
191
232
|
sessionsTools.getTools().forEach((tool) => {
|
|
192
233
|
this.tools.set(tool.definition.name, tool);
|
|
193
234
|
});
|
|
194
235
|
// Analytics tools (carbon, provenance, diligence)
|
|
195
|
-
const analyticsTools = new AnalyticsTools(
|
|
236
|
+
const analyticsTools = new AnalyticsTools(pica);
|
|
196
237
|
analyticsTools.getTools().forEach((tool) => {
|
|
197
238
|
this.tools.set(tool.definition.name, tool);
|
|
198
239
|
});
|
|
199
240
|
// Notifications tools
|
|
200
|
-
const notificationsTools = new NotificationsTools(
|
|
241
|
+
const notificationsTools = new NotificationsTools(pica);
|
|
201
242
|
notificationsTools.getTools().forEach((tool) => {
|
|
202
243
|
this.tools.set(tool.definition.name, tool);
|
|
203
244
|
});
|
|
204
245
|
// Notes tools (catalog notes on works, people, general observations)
|
|
205
|
-
const notesTools = new NotesTools(
|
|
246
|
+
const notesTools = new NotesTools(pica);
|
|
206
247
|
notesTools.getTools().forEach((tool) => {
|
|
207
248
|
this.tools.set(tool.definition.name, tool);
|
|
208
249
|
});
|
|
209
250
|
// Team management tools (invite, list, update, remove members)
|
|
210
|
-
const teamTools = new TeamTools(
|
|
251
|
+
const teamTools = new TeamTools(pica);
|
|
211
252
|
teamTools.getTools().forEach((tool) => {
|
|
212
253
|
this.tools.set(tool.definition.name, tool);
|
|
213
254
|
});
|
|
214
255
|
// Projects tools (albums, EPs, compilations — grouping works)
|
|
215
|
-
const projectsTools = new ProjectsTools(
|
|
256
|
+
const projectsTools = new ProjectsTools(pica);
|
|
216
257
|
projectsTools.getTools().forEach((tool) => {
|
|
217
258
|
this.tools.set(tool.definition.name, tool);
|
|
218
259
|
});
|
|
219
260
|
// Releases tools (albums, EPs, singles with dates, labels, UPCs)
|
|
220
|
-
const releasesTools = new ReleasesTools(
|
|
261
|
+
const releasesTools = new ReleasesTools(pica);
|
|
221
262
|
releasesTools.getTools().forEach((tool) => {
|
|
222
263
|
this.tools.set(tool.definition.name, tool);
|
|
223
264
|
});
|
|
224
265
|
// Calendar tools
|
|
225
|
-
const calendarTools = new CalendarTools(
|
|
266
|
+
const calendarTools = new CalendarTools(pica);
|
|
226
267
|
calendarTools.getTools().forEach((tool) => {
|
|
227
268
|
this.tools.set(tool.definition.name, tool);
|
|
228
269
|
});
|
|
229
270
|
// Split sheets & recording splits tools
|
|
230
|
-
const splitSheetsTools = new SplitSheetsTools(
|
|
271
|
+
const splitSheetsTools = new SplitSheetsTools(pica);
|
|
231
272
|
splitSheetsTools.getTools().forEach((tool) => {
|
|
232
273
|
this.tools.set(tool.definition.name, tool);
|
|
233
274
|
});
|
|
234
275
|
// Agreement types tools (templates, producer agreements, work-for-hire)
|
|
235
|
-
const agreementTypesTools = new AgreementTypesTools(
|
|
276
|
+
const agreementTypesTools = new AgreementTypesTools(pica);
|
|
236
277
|
agreementTypesTools.getTools().forEach((tool) => {
|
|
237
278
|
this.tools.set(tool.definition.name, tool);
|
|
238
279
|
});
|
|
239
280
|
// Dashboard & discovery tools
|
|
240
|
-
const dashboardTools = new DashboardTools(
|
|
281
|
+
const dashboardTools = new DashboardTools(pica);
|
|
241
282
|
dashboardTools.getTools().forEach((tool) => {
|
|
242
283
|
this.tools.set(tool.definition.name, tool);
|
|
243
284
|
});
|
|
244
285
|
// Integrations tools (connection status)
|
|
245
|
-
const integrationsTools = new IntegrationsTools(
|
|
286
|
+
const integrationsTools = new IntegrationsTools(pica);
|
|
246
287
|
integrationsTools.getTools().forEach((tool) => {
|
|
247
288
|
this.tools.set(tool.definition.name, tool);
|
|
248
289
|
});
|
|
249
290
|
// Settings tools (credits history, storage, org profile)
|
|
250
|
-
const settingsTools = new SettingsTools(
|
|
291
|
+
const settingsTools = new SettingsTools(pica);
|
|
251
292
|
settingsTools.getTools().forEach((tool) => {
|
|
252
293
|
this.tools.set(tool.definition.name, tool);
|
|
253
294
|
});
|
|
254
295
|
// Royalties & statements tools (ADR-139 Step 2)
|
|
255
|
-
const royaltiesTools = new RoyaltiesTools(
|
|
296
|
+
const royaltiesTools = new RoyaltiesTools(pica);
|
|
256
297
|
royaltiesTools.getTools().forEach((tool) => {
|
|
257
298
|
this.tools.set(tool.definition.name, tool);
|
|
258
299
|
});
|
|
259
300
|
// Share links tools (ADR-139 Step 3)
|
|
260
|
-
const shareLinksTools = new ShareLinksTools(
|
|
301
|
+
const shareLinksTools = new ShareLinksTools(pica);
|
|
261
302
|
shareLinksTools.getTools().forEach((tool) => {
|
|
262
303
|
this.tools.set(tool.definition.name, tool);
|
|
263
304
|
});
|
|
264
305
|
// Disputes tools (ADR-139 Step 3)
|
|
265
|
-
const disputesTools = new DisputesTools(
|
|
306
|
+
const disputesTools = new DisputesTools(pica);
|
|
266
307
|
disputesTools.getTools().forEach((tool) => {
|
|
267
308
|
this.tools.set(tool.definition.name, tool);
|
|
268
309
|
});
|
|
310
|
+
// Custody tools (ADR-158 Plan C)
|
|
311
|
+
const custodyTools = new CustodyTools(pica);
|
|
312
|
+
custodyTools.getTools().forEach((tool) => {
|
|
313
|
+
this.tools.set(tool.definition.name, tool);
|
|
314
|
+
});
|
|
269
315
|
// Purchases tools (credit checkout)
|
|
270
|
-
const purchasesTools = new PurchasesTools(
|
|
316
|
+
const purchasesTools = new PurchasesTools(pica);
|
|
271
317
|
purchasesTools.getTools().forEach((tool) => {
|
|
272
318
|
this.tools.set(tool.definition.name, tool);
|
|
273
319
|
});
|
|
274
320
|
// Telegram tools (user notification channel)
|
|
275
|
-
const telegramTools = new TelegramTools(
|
|
321
|
+
const telegramTools = new TelegramTools(pica);
|
|
276
322
|
telegramTools.getTools().forEach((tool) => {
|
|
277
323
|
this.tools.set(tool.definition.name, tool);
|
|
278
324
|
});
|
|
325
|
+
// Publishers tools (lookup + create)
|
|
326
|
+
const publishersTools = new PublishersTools(pica);
|
|
327
|
+
publishersTools.getTools().forEach((tool) => {
|
|
328
|
+
this.tools.set(tool.definition.name, tool);
|
|
329
|
+
});
|
|
330
|
+
// App tools (MCP Apps — interactive UI cards)
|
|
331
|
+
const appTools = new AppTools(pica);
|
|
332
|
+
appTools.getTools().forEach((tool) => {
|
|
333
|
+
this.tools.set(tool.definition.name, tool);
|
|
334
|
+
});
|
|
335
|
+
// Discovery meta-tools — registered last so pica_tool_details can look up
|
|
336
|
+
// all tool definitions. Only active when discoveryMode is enabled.
|
|
337
|
+
if (this.config?.discoveryMode) {
|
|
338
|
+
const discoveryTools = new DiscoveryTools((name, args) => this.executeTool(name, args), (name) => this.tools.get(name));
|
|
339
|
+
discoveryTools.getTools().forEach((tool) => {
|
|
340
|
+
this.tools.set(tool.definition.name, tool);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
279
343
|
}
|
|
280
344
|
// ── Write-safety classification ──
|
|
281
345
|
// Destructive tools require confirmation before AND summary after.
|
|
@@ -320,25 +384,18 @@ export class ToolRegistry {
|
|
|
320
384
|
// Read-only tools that match mutating patterns by name but are actually safe
|
|
321
385
|
static SAFE_OVERRIDES = new Set([
|
|
322
386
|
"pica_collaborators_invites_list",
|
|
323
|
-
"pica_completeness_low",
|
|
324
387
|
"pica_enrichment_candidates",
|
|
325
388
|
"pica_enrichment_compare",
|
|
326
|
-
"pica_enrichment_status",
|
|
327
389
|
"pica_find_duplicates",
|
|
328
390
|
"pica_import_analyze",
|
|
329
391
|
"pica_import_validate",
|
|
330
392
|
"pica_import_fields",
|
|
331
393
|
"pica_import_template",
|
|
332
|
-
"
|
|
333
|
-
"
|
|
334
|
-
"
|
|
335
|
-
"pica_send_list",
|
|
336
|
-
"pica_send_pending",
|
|
394
|
+
"pica_import_documents_query",
|
|
395
|
+
"pica_import_documents_inspect",
|
|
396
|
+
"pica_send_query",
|
|
337
397
|
"pica_share_links_list",
|
|
338
|
-
"pica_work_completeness",
|
|
339
398
|
]);
|
|
340
|
-
static DESTRUCTIVE_PREFIX = "⚠️ DESTRUCTIVE: Before calling this tool, you MUST describe exactly what will be deleted/merged to the user and get explicit confirmation. After execution, confirm what was done and what was affected. ";
|
|
341
|
-
static MUTATING_PREFIX = "✏️ WRITE OPERATION: Before calling this tool, briefly tell the user what you're about to do. After execution, confirm what was created/changed. ";
|
|
342
399
|
classifyTool(name) {
|
|
343
400
|
if (ToolRegistry.SAFE_OVERRIDES.has(name)) {
|
|
344
401
|
return "safe";
|
|
@@ -352,37 +409,344 @@ export class ToolRegistry {
|
|
|
352
409
|
return "safe";
|
|
353
410
|
}
|
|
354
411
|
/**
|
|
355
|
-
* List all available tools with write-safety prefixes injected
|
|
412
|
+
* List all available tools with write-safety prefixes injected.
|
|
413
|
+
* When discoveryMode is enabled, only the 5 handshake-visible tools are returned.
|
|
414
|
+
* All other tools remain registered and callable via pica_execute.
|
|
356
415
|
*/
|
|
357
416
|
listTools() {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
417
|
+
let toolsToList = Array.from(this.tools.values());
|
|
418
|
+
if (this.config?.discoveryMode) {
|
|
419
|
+
const META_TOOL_NAMES = new Set([
|
|
420
|
+
"pica_sign_in",
|
|
421
|
+
"pica_sign_out",
|
|
422
|
+
"pica_discover",
|
|
423
|
+
"pica_tool_details",
|
|
424
|
+
"pica_execute",
|
|
425
|
+
]);
|
|
426
|
+
toolsToList = toolsToList.filter((t) => META_TOOL_NAMES.has(t.definition.name));
|
|
427
|
+
}
|
|
428
|
+
return toolsToList.map((tool) => {
|
|
429
|
+
let definition = tool.definition;
|
|
430
|
+
const metadata = getToolMetadata(definition.name);
|
|
431
|
+
// Inject confirmation_token into destructive tool schemas
|
|
432
|
+
const isDestructive = metadata?.risk === "destructive" ||
|
|
433
|
+
(!metadata && this.classifyTool(definition.name) === "destructive");
|
|
434
|
+
if (isDestructive) {
|
|
435
|
+
definition = {
|
|
436
|
+
...definition,
|
|
437
|
+
inputSchema: {
|
|
438
|
+
...definition.inputSchema,
|
|
439
|
+
properties: {
|
|
440
|
+
...definition.inputSchema.properties,
|
|
441
|
+
confirmation_token: {
|
|
442
|
+
type: "string",
|
|
443
|
+
description: "Confirmation token from a previous call. Required to execute destructive operations. " +
|
|
444
|
+
"Call this tool once without a token to get a preview and token, then call again with the token to confirm.",
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
};
|
|
362
449
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
450
|
+
// Normalize _meta.ui.resourceUri → legacy "ui/resourceUri" key for older MCP Apps clients
|
|
451
|
+
if (definition._meta?.ui?.resourceUri &&
|
|
452
|
+
!definition._meta["ui/resourceUri"]) {
|
|
453
|
+
definition = {
|
|
454
|
+
...definition,
|
|
455
|
+
_meta: {
|
|
456
|
+
...definition._meta,
|
|
457
|
+
"ui/resourceUri": definition._meta.ui.resourceUri,
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
if (metadata) {
|
|
462
|
+
return {
|
|
463
|
+
...definition,
|
|
464
|
+
description: injectMetadataIntoDescription(tool.definition.description, metadata),
|
|
465
|
+
annotations: {
|
|
466
|
+
title: metadata.display_name,
|
|
467
|
+
readOnlyHint: metadata.risk === "safe",
|
|
468
|
+
destructiveHint: metadata.risk === "destructive",
|
|
469
|
+
idempotentHint: metadata.risk === "safe" && metadata.retry_safe,
|
|
470
|
+
openWorldHint: false,
|
|
471
|
+
},
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
// Fallback to old pattern-matching for any tool without explicit metadata
|
|
475
|
+
const classification = this.classifyTool(definition.name);
|
|
476
|
+
const annotations = {
|
|
477
|
+
title: definition.name,
|
|
478
|
+
readOnlyHint: classification === "safe",
|
|
479
|
+
destructiveHint: classification === "destructive",
|
|
480
|
+
idempotentHint: false,
|
|
481
|
+
openWorldHint: false,
|
|
369
482
|
};
|
|
483
|
+
// No risk prefix needed — annotations handle this structurally
|
|
484
|
+
return { ...definition, annotations };
|
|
370
485
|
});
|
|
371
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Build a human-readable preview of what a destructive operation will affect.
|
|
489
|
+
*/
|
|
490
|
+
async buildDestructivePreview(name, args) {
|
|
491
|
+
const metadata = getToolMetadata(name);
|
|
492
|
+
const displayName = metadata?.display_name || name;
|
|
493
|
+
// buildDestructivePreview is only called during tool execution,
|
|
494
|
+
// which is gated behind lobby mode check — pica is always available here.
|
|
495
|
+
const pica = this.pica;
|
|
496
|
+
try {
|
|
497
|
+
switch (name) {
|
|
498
|
+
case "pica_works_delete": {
|
|
499
|
+
const work = await pica.works.get(args.id).catch(() => null);
|
|
500
|
+
const title = work?.title || args.id;
|
|
501
|
+
return {
|
|
502
|
+
action: "delete work",
|
|
503
|
+
target: title,
|
|
504
|
+
warning: "this will remove linked credits, agreement links, and audio files. recordings will be unlinked. this cannot be undone for works without verified identifiers.",
|
|
505
|
+
display_name: displayName,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
case "pica_works_bulk_delete": {
|
|
509
|
+
const count = args.ids?.length || 0;
|
|
510
|
+
return {
|
|
511
|
+
action: "bulk delete works",
|
|
512
|
+
target: `${count} works`,
|
|
513
|
+
warning: `this will delete ${count} work(s) and cascade to their credits and agreement links. this cannot be undone for works without verified identifiers.`,
|
|
514
|
+
display_name: displayName,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
case "pica_people_delete": {
|
|
518
|
+
const person = await pica.people.get(args.id).catch(() => null);
|
|
519
|
+
const personName = person?.first_name
|
|
520
|
+
? `${person.first_name} ${person.last_name || ""}`.trim()
|
|
521
|
+
: args.id;
|
|
522
|
+
return {
|
|
523
|
+
action: "remove person",
|
|
524
|
+
target: personName,
|
|
525
|
+
warning: "the person record will be soft-deleted. credits and agreements will remain linked.",
|
|
526
|
+
display_name: displayName,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
case "pica_agreements_delete": {
|
|
530
|
+
const agreementResult = await pica.agreements
|
|
531
|
+
.get(args.id)
|
|
532
|
+
.catch(() => null);
|
|
533
|
+
const title = agreementResult?.agreement?.title || args.id;
|
|
534
|
+
return {
|
|
535
|
+
action: "delete agreement",
|
|
536
|
+
target: title,
|
|
537
|
+
warning: "this will remove all linked work and party records. this cannot be undone.",
|
|
538
|
+
display_name: displayName,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
case "pica_merge_duplicates": {
|
|
542
|
+
const loserCount = args.loser_ids?.length || 0;
|
|
543
|
+
const entityType = args.entity_type || "entities";
|
|
544
|
+
return {
|
|
545
|
+
action: `merge ${entityType}`,
|
|
546
|
+
target: `${loserCount} ${entityType} into winner`,
|
|
547
|
+
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.`,
|
|
548
|
+
display_name: displayName,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
case "pica_projects_delete": {
|
|
552
|
+
const project = await pica.projects?.get(args.id).catch(() => null);
|
|
553
|
+
const projectName = project?.name || args.id;
|
|
554
|
+
return {
|
|
555
|
+
action: "delete project",
|
|
556
|
+
target: projectName,
|
|
557
|
+
warning: "linked works will be unlinked but not deleted. this cannot be undone.",
|
|
558
|
+
display_name: displayName,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
case "pica_team_remove": {
|
|
562
|
+
return {
|
|
563
|
+
action: "remove team member",
|
|
564
|
+
target: args.id,
|
|
565
|
+
warning: "their user account will be permanently deleted. works and agreements they contributed will remain. this cannot be undone.",
|
|
566
|
+
display_name: displayName,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
default: {
|
|
570
|
+
return {
|
|
571
|
+
action: displayName,
|
|
572
|
+
target: args.id || "unknown",
|
|
573
|
+
warning: "this cannot be undone.",
|
|
574
|
+
display_name: displayName,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
catch {
|
|
580
|
+
return {
|
|
581
|
+
action: displayName,
|
|
582
|
+
target: args.id || "unknown",
|
|
583
|
+
warning: "this cannot be undone.",
|
|
584
|
+
display_name: displayName,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Sanitize tool parameters for audit logging.
|
|
590
|
+
* Strips confirmation tokens and truncates large string values.
|
|
591
|
+
*/
|
|
592
|
+
sanitizeParams(args) {
|
|
593
|
+
const sanitized = { ...args };
|
|
594
|
+
delete sanitized.confirmation_token;
|
|
595
|
+
for (const [key, value] of Object.entries(sanitized)) {
|
|
596
|
+
if (typeof value === "string" && value.length > 500) {
|
|
597
|
+
sanitized[key] = value.substring(0, 500) + "...[truncated]";
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return sanitized;
|
|
601
|
+
}
|
|
372
602
|
/**
|
|
373
603
|
* Execute a tool by name
|
|
374
604
|
*/
|
|
375
605
|
async executeTool(name, args) {
|
|
606
|
+
const startTime = Date.now();
|
|
376
607
|
const tool = this.tools.get(name);
|
|
377
608
|
if (!tool) {
|
|
378
609
|
throw new ToolExecutionError(`Tool not found: ${name}`);
|
|
379
610
|
}
|
|
611
|
+
const metadata = getToolMetadata(name);
|
|
612
|
+
// ── Session freshness gate (ADR-151) ──
|
|
613
|
+
if (metadata?.risk === "destructive" &&
|
|
614
|
+
this.config &&
|
|
615
|
+
!this.config.lobbyMode) {
|
|
616
|
+
const creds = readCredentials(this.config.credentialsPath);
|
|
617
|
+
if (creds?.authenticated_at) {
|
|
618
|
+
const authAge = Date.now() - new Date(creds.authenticated_at).getTime();
|
|
619
|
+
const sevenDays = 7 * 24 * 60 * 60 * 1000;
|
|
620
|
+
if (authAge > sevenDays) {
|
|
621
|
+
return {
|
|
622
|
+
content: [
|
|
623
|
+
{
|
|
624
|
+
type: "text",
|
|
625
|
+
text: 'this action requires re-verification — say "sign me in" to confirm your identity',
|
|
626
|
+
},
|
|
627
|
+
],
|
|
628
|
+
isError: true,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
// ── Destructive confirmation gate ──
|
|
634
|
+
if (metadata?.risk === "destructive") {
|
|
635
|
+
const confirmationToken = args.confirmation_token;
|
|
636
|
+
if (!confirmationToken) {
|
|
637
|
+
// First call — generate preview and return confirmation challenge
|
|
638
|
+
const token = generateConfirmationToken(name, args);
|
|
639
|
+
const preview = await this.buildDestructivePreview(name, args);
|
|
640
|
+
const response = {
|
|
641
|
+
content: [
|
|
642
|
+
{
|
|
643
|
+
type: "text",
|
|
644
|
+
text: JSON.stringify({
|
|
645
|
+
status: "confirmation_required",
|
|
646
|
+
confirmation_token: token,
|
|
647
|
+
expires_in: 120,
|
|
648
|
+
preview,
|
|
649
|
+
}, null, 2),
|
|
650
|
+
},
|
|
651
|
+
],
|
|
652
|
+
structuredContent: {
|
|
653
|
+
status: "confirmation_required",
|
|
654
|
+
confirmation_token: token,
|
|
655
|
+
expires_in: 120,
|
|
656
|
+
preview,
|
|
657
|
+
},
|
|
658
|
+
};
|
|
659
|
+
// Fire-and-forget audit for confirmation challenge
|
|
660
|
+
this.auditLogger
|
|
661
|
+
?.logToolExecution({
|
|
662
|
+
tool_name: name,
|
|
663
|
+
tool_category: metadata?.category || "unknown",
|
|
664
|
+
risk_level: "destructive",
|
|
665
|
+
parameters: this.sanitizeParams(args),
|
|
666
|
+
result_status: "confirmation_required",
|
|
667
|
+
confirmation_token: token,
|
|
668
|
+
execution_time_ms: Date.now() - startTime,
|
|
669
|
+
caller_identity: this.callerContext.callerIdentity,
|
|
670
|
+
transport: this.callerContext.transport,
|
|
671
|
+
})
|
|
672
|
+
.catch(() => { }); // Never block on audit
|
|
673
|
+
return response;
|
|
674
|
+
}
|
|
675
|
+
// Second call — validate token
|
|
676
|
+
const validation = validateAndConsumeToken(confirmationToken, name, args);
|
|
677
|
+
if (!validation.valid) {
|
|
678
|
+
return {
|
|
679
|
+
content: [
|
|
680
|
+
{
|
|
681
|
+
type: "text",
|
|
682
|
+
text: JSON.stringify({
|
|
683
|
+
error: validation.reason?.includes("expired")
|
|
684
|
+
? "CONFIRMATION_EXPIRED"
|
|
685
|
+
: "CONFIRMATION_INVALID",
|
|
686
|
+
message: validation.reason,
|
|
687
|
+
retry_safe: false,
|
|
688
|
+
suggestion: "start the operation again without the confirmation token",
|
|
689
|
+
}, null, 2),
|
|
690
|
+
},
|
|
691
|
+
],
|
|
692
|
+
isError: true,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
// Token valid — fall through to execute
|
|
696
|
+
}
|
|
697
|
+
// ── Normal execution ──
|
|
380
698
|
try {
|
|
381
|
-
|
|
699
|
+
const result = await tool.executor(args);
|
|
700
|
+
// Guard response size — truncate oversized payloads with recovery hint
|
|
701
|
+
if (result?.content) {
|
|
702
|
+
for (const block of result.content) {
|
|
703
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
704
|
+
block.text = guardResponseSize(block.text, name);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
// Fire-and-forget audit
|
|
709
|
+
this.auditLogger
|
|
710
|
+
?.logToolExecution({
|
|
711
|
+
tool_name: name,
|
|
712
|
+
tool_category: metadata?.category || "unknown",
|
|
713
|
+
risk_level: metadata?.risk || this.classifyTool(name),
|
|
714
|
+
parameters: this.sanitizeParams(args),
|
|
715
|
+
result_status: "success",
|
|
716
|
+
execution_time_ms: Date.now() - startTime,
|
|
717
|
+
caller_identity: this.callerContext.callerIdentity,
|
|
718
|
+
transport: this.callerContext.transport,
|
|
719
|
+
})
|
|
720
|
+
.catch(() => { }); // Never block on audit
|
|
721
|
+
return result;
|
|
382
722
|
}
|
|
383
723
|
catch (error) {
|
|
724
|
+
const formatted = formatError(error);
|
|
725
|
+
const parsed = JSON.parse(formatted.text);
|
|
726
|
+
// Attach recovery hint if available for this tool + error code
|
|
727
|
+
const hint = getRecoveryHint(name, parsed.error);
|
|
728
|
+
if (hint) {
|
|
729
|
+
parsed.suggestion = hint.suggestion;
|
|
730
|
+
if (hint.next_tool) {
|
|
731
|
+
parsed.next_tool = hint.next_tool;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
// Fire-and-forget audit for errors
|
|
735
|
+
this.auditLogger
|
|
736
|
+
?.logToolExecution({
|
|
737
|
+
tool_name: name,
|
|
738
|
+
tool_category: metadata?.category || "unknown",
|
|
739
|
+
risk_level: metadata?.risk || this.classifyTool(name),
|
|
740
|
+
parameters: this.sanitizeParams(args),
|
|
741
|
+
result_status: "error",
|
|
742
|
+
error_code: parsed.error,
|
|
743
|
+
execution_time_ms: Date.now() - startTime,
|
|
744
|
+
caller_identity: this.callerContext.callerIdentity,
|
|
745
|
+
transport: this.callerContext.transport,
|
|
746
|
+
})
|
|
747
|
+
.catch(() => { }); // Never block on audit
|
|
384
748
|
return {
|
|
385
|
-
content: [
|
|
749
|
+
content: [{ type: "text", text: JSON.stringify(parsed, null, 2) }],
|
|
386
750
|
isError: true,
|
|
387
751
|
};
|
|
388
752
|
}
|