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