@withpica/mcp-server 2.52.0 → 2.52.2
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/CHANGELOG.md +93 -0
- package/dist/prompts/creator-question-atlas.d.ts +48 -0
- package/dist/prompts/creator-question-atlas.d.ts.map +1 -0
- package/dist/prompts/creator-question-atlas.js +618 -0
- package/dist/prompts/creator-question-atlas.js.map +1 -0
- package/dist/prompts/index.d.ts +32 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +235 -2
- package/dist/prompts/index.js.map +1 -1
- package/dist/resources/index.d.ts +10 -0
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +134 -1
- package/dist/resources/index.js.map +1 -1
- package/dist/server-instructions.d.ts +5 -3
- package/dist/server-instructions.d.ts.map +1 -1
- package/dist/server-instructions.js +9 -5
- package/dist/server-instructions.js.map +1 -1
- package/dist/server.d.ts +26 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +108 -10
- package/dist/server.js.map +1 -1
- package/dist/skills/index.d.ts +42 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +59 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/skills.generated.d.ts +25 -0
- package/dist/skills/skills.generated.d.ts.map +1 -0
- package/dist/skills/skills.generated.js +86 -0
- package/dist/skills/skills.generated.js.map +1 -0
- package/dist/tools/access-simulate.d.ts +23 -0
- package/dist/tools/access-simulate.d.ts.map +1 -0
- package/dist/tools/access-simulate.js +165 -0
- package/dist/tools/access-simulate.js.map +1 -0
- package/dist/tools/agent-identity.d.ts.map +1 -1
- package/dist/tools/agent-identity.js +15 -0
- package/dist/tools/agent-identity.js.map +1 -1
- package/dist/tools/agreement-types.d.ts.map +1 -1
- package/dist/tools/agreement-types.js +24 -0
- package/dist/tools/agreement-types.js.map +1 -1
- package/dist/tools/agreements.d.ts.map +1 -1
- package/dist/tools/agreements.js +21 -3
- package/dist/tools/agreements.js.map +1 -1
- package/dist/tools/analytics.d.ts.map +1 -1
- package/dist/tools/analytics.js +19 -1
- package/dist/tools/analytics.js.map +1 -1
- package/dist/tools/app-tools.d.ts.map +1 -1
- package/dist/tools/app-tools.js +11 -2
- package/dist/tools/app-tools.js.map +1 -1
- package/dist/tools/assets.d.ts.map +1 -1
- package/dist/tools/assets.js +33 -0
- package/dist/tools/assets.js.map +1 -1
- package/dist/tools/audio-files.d.ts +5 -0
- package/dist/tools/audio-files.d.ts.map +1 -1
- package/dist/tools/audio-files.js +91 -0
- package/dist/tools/audio-files.js.map +1 -1
- package/dist/tools/audit.d.ts.map +1 -1
- package/dist/tools/audit.js +11 -2
- package/dist/tools/audit.js.map +1 -1
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +6 -0
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/bulk.d.ts +4 -0
- package/dist/tools/bulk.d.ts.map +1 -1
- package/dist/tools/bulk.js +304 -0
- package/dist/tools/bulk.js.map +1 -1
- package/dist/tools/calendar.d.ts.map +1 -1
- package/dist/tools/calendar.js +3 -0
- package/dist/tools/calendar.js.map +1 -1
- package/dist/tools/collaborators.d.ts.map +1 -1
- package/dist/tools/collaborators.js +24 -3
- package/dist/tools/collaborators.js.map +1 -1
- package/dist/tools/comparisons.d.ts.map +1 -1
- package/dist/tools/comparisons.js +6 -0
- package/dist/tools/comparisons.js.map +1 -1
- package/dist/tools/credits.d.ts +18 -0
- package/dist/tools/credits.d.ts.map +1 -1
- package/dist/tools/credits.js +344 -4
- package/dist/tools/credits.js.map +1 -1
- package/dist/tools/custody.d.ts.map +1 -1
- package/dist/tools/custody.js +23 -2
- package/dist/tools/custody.js.map +1 -1
- package/dist/tools/dashboard.d.ts.map +1 -1
- package/dist/tools/dashboard.js +43 -7
- package/dist/tools/dashboard.js.map +1 -1
- package/dist/tools/directory.d.ts.map +1 -1
- package/dist/tools/directory.js +3 -0
- package/dist/tools/directory.js.map +1 -1
- package/dist/tools/discovery.d.ts.map +1 -1
- package/dist/tools/discovery.js +99 -3
- package/dist/tools/discovery.js.map +1 -1
- package/dist/tools/disputes.d.ts.map +1 -1
- package/dist/tools/disputes.js +4 -1
- package/dist/tools/disputes.js.map +1 -1
- package/dist/tools/documents.d.ts.map +1 -1
- package/dist/tools/documents.js +3 -0
- package/dist/tools/documents.js.map +1 -1
- package/dist/tools/duplicates.d.ts.map +1 -1
- package/dist/tools/duplicates.js +6 -0
- package/dist/tools/duplicates.js.map +1 -1
- package/dist/tools/enrichment.d.ts.map +1 -1
- package/dist/tools/enrichment.js +33 -0
- package/dist/tools/enrichment.js.map +1 -1
- package/dist/tools/explainability.d.ts +24 -0
- package/dist/tools/explainability.d.ts.map +1 -0
- package/dist/tools/explainability.js +137 -0
- package/dist/tools/explainability.js.map +1 -0
- package/dist/tools/exports.d.ts.map +1 -1
- package/dist/tools/exports.js +18 -3
- package/dist/tools/exports.js.map +1 -1
- package/dist/tools/feedback.d.ts.map +1 -1
- package/dist/tools/feedback.js +3 -0
- package/dist/tools/feedback.js.map +1 -1
- package/dist/tools/files.d.ts.map +1 -1
- package/dist/tools/files.js +22 -0
- package/dist/tools/files.js.map +1 -1
- package/dist/tools/groups.d.ts.map +1 -1
- package/dist/tools/groups.js +12 -0
- package/dist/tools/groups.js.map +1 -1
- package/dist/tools/import-documents.d.ts.map +1 -1
- package/dist/tools/import-documents.js +10 -1
- package/dist/tools/import-documents.js.map +1 -1
- package/dist/tools/import.d.ts.map +1 -1
- package/dist/tools/import.js +65 -18
- package/dist/tools/import.js.map +1 -1
- package/dist/tools/index.d.ts +142 -6
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +289 -114
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/integrations.d.ts.map +1 -1
- package/dist/tools/integrations.js +28 -8
- package/dist/tools/integrations.js.map +1 -1
- package/dist/tools/labels.d.ts.map +1 -1
- package/dist/tools/labels.js +3 -0
- package/dist/tools/labels.js.map +1 -1
- package/dist/tools/licensing.d.ts.map +1 -1
- package/dist/tools/licensing.js +15 -0
- package/dist/tools/licensing.js.map +1 -1
- package/dist/tools/memory.d.ts.map +1 -1
- package/dist/tools/memory.js +15 -3
- package/dist/tools/memory.js.map +1 -1
- package/dist/tools/metadata.d.ts.map +1 -1
- package/dist/tools/metadata.js +112 -6
- package/dist/tools/metadata.js.map +1 -1
- package/dist/tools/multimedia.d.ts.map +1 -1
- package/dist/tools/multimedia.js +15 -0
- package/dist/tools/multimedia.js.map +1 -1
- package/dist/tools/my-recent-questions.d.ts +25 -0
- package/dist/tools/my-recent-questions.d.ts.map +1 -0
- package/dist/tools/my-recent-questions.js +186 -0
- package/dist/tools/my-recent-questions.js.map +1 -0
- package/dist/tools/my-reported-issues.d.ts.map +1 -1
- package/dist/tools/my-reported-issues.js +3 -0
- package/dist/tools/my-reported-issues.js.map +1 -1
- package/dist/tools/notes.d.ts.map +1 -1
- package/dist/tools/notes.js +12 -0
- package/dist/tools/notes.js.map +1 -1
- package/dist/tools/notifications.d.ts.map +1 -1
- package/dist/tools/notifications.js +25 -1
- package/dist/tools/notifications.js.map +1 -1
- package/dist/tools/onboarding.d.ts.map +1 -1
- package/dist/tools/onboarding.js +3 -0
- package/dist/tools/onboarding.js.map +1 -1
- package/dist/tools/people.d.ts +4 -0
- package/dist/tools/people.d.ts.map +1 -1
- package/dist/tools/people.js +58 -1
- package/dist/tools/people.js.map +1 -1
- package/dist/tools/projects.d.ts.map +1 -1
- package/dist/tools/projects.js +18 -0
- package/dist/tools/projects.js.map +1 -1
- package/dist/tools/public-filter.d.ts.map +1 -1
- package/dist/tools/public-filter.js +6 -1
- package/dist/tools/public-filter.js.map +1 -1
- package/dist/tools/publishers.d.ts.map +1 -1
- package/dist/tools/publishers.js +6 -0
- package/dist/tools/publishers.js.map +1 -1
- package/dist/tools/recordings.d.ts.map +1 -1
- package/dist/tools/recordings.js +15 -0
- package/dist/tools/recordings.js.map +1 -1
- package/dist/tools/recovery-hints.d.ts.map +1 -1
- package/dist/tools/recovery-hints.js +105 -0
- package/dist/tools/recovery-hints.js.map +1 -1
- package/dist/tools/release-rich.d.ts.map +1 -1
- package/dist/tools/release-rich.js +4 -2
- package/dist/tools/release-rich.js.map +1 -1
- package/dist/tools/releases.d.ts.map +1 -1
- package/dist/tools/releases.js +55 -0
- package/dist/tools/releases.js.map +1 -1
- package/dist/tools/report-issue.d.ts.map +1 -1
- package/dist/tools/report-issue.js +3 -0
- package/dist/tools/report-issue.js.map +1 -1
- package/dist/tools/royalties.d.ts.map +1 -1
- package/dist/tools/royalties.js +18 -3
- package/dist/tools/royalties.js.map +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +10 -1
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/send.d.ts.map +1 -1
- package/dist/tools/send.js +9 -0
- package/dist/tools/send.js.map +1 -1
- package/dist/tools/sessions.d.ts.map +1 -1
- package/dist/tools/sessions.js +12 -0
- package/dist/tools/sessions.js.map +1 -1
- package/dist/tools/settings.d.ts.map +1 -1
- package/dist/tools/settings.js +30 -3
- package/dist/tools/settings.js.map +1 -1
- package/dist/tools/share-links.d.ts.map +1 -1
- package/dist/tools/share-links.js +15 -0
- package/dist/tools/share-links.js.map +1 -1
- package/dist/tools/share-send.d.ts +28 -0
- package/dist/tools/share-send.d.ts.map +1 -0
- package/dist/tools/share-send.js +131 -0
- package/dist/tools/share-send.js.map +1 -0
- package/dist/tools/sharing.d.ts +29 -0
- package/dist/tools/sharing.d.ts.map +1 -0
- package/dist/tools/sharing.js +131 -0
- package/dist/tools/sharing.js.map +1 -0
- package/dist/tools/signup.d.ts.map +1 -1
- package/dist/tools/signup.js +3 -0
- package/dist/tools/signup.js.map +1 -1
- package/dist/tools/skills.d.ts +25 -0
- package/dist/tools/skills.d.ts.map +1 -0
- package/dist/tools/skills.js +144 -0
- package/dist/tools/skills.js.map +1 -0
- package/dist/tools/split-sheets.d.ts.map +1 -1
- package/dist/tools/split-sheets.js +22 -1
- package/dist/tools/split-sheets.js.map +1 -1
- package/dist/tools/storage-config.d.ts.map +1 -1
- package/dist/tools/storage-config.js +6 -0
- package/dist/tools/storage-config.js.map +1 -1
- package/dist/tools/subscription.d.ts.map +1 -1
- package/dist/tools/subscription.js +9 -10
- package/dist/tools/subscription.js.map +1 -1
- package/dist/tools/sync-placements.d.ts.map +1 -1
- package/dist/tools/sync-placements.js +20 -2
- package/dist/tools/sync-placements.js.map +1 -1
- package/dist/tools/team.d.ts.map +1 -1
- package/dist/tools/team.js +15 -0
- package/dist/tools/team.js.map +1 -1
- package/dist/tools/telegram.d.ts.map +1 -1
- package/dist/tools/telegram.js +9 -0
- package/dist/tools/telegram.js.map +1 -1
- package/dist/tools/uploads.d.ts.map +1 -1
- package/dist/tools/uploads.js +6 -0
- package/dist/tools/uploads.js.map +1 -1
- package/dist/tools/works.d.ts +4 -0
- package/dist/tools/works.d.ts.map +1 -1
- package/dist/tools/works.js +83 -3
- package/dist/tools/works.js.map +1 -1
- package/package.json +8 -6
- package/scripts/build-skills.ts +229 -0
- package/server.json +2 -2
package/dist/tools/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { WorksTools } from "./works.js";
|
|
3
3
|
import { PeopleTools } from "./people.js";
|
|
4
4
|
import { GroupsTools } from "./groups.js";
|
|
5
|
+
import { ShareSendTools } from "./share-send.js";
|
|
5
6
|
import { RecordingsTools } from "./recordings.js";
|
|
6
7
|
import { SearchTools } from "./search.js";
|
|
7
8
|
import { LicensingTools } from "./licensing.js";
|
|
@@ -10,6 +11,7 @@ import { AudioFilesTools } from "./audio-files.js";
|
|
|
10
11
|
import { MultimediaTools } from "./multimedia.js";
|
|
11
12
|
import { AgreementsTools } from "./agreements.js";
|
|
12
13
|
import { MemoryTools } from "./memory.js";
|
|
14
|
+
import { SkillsTools } from "./skills.js";
|
|
13
15
|
import { EnrichmentTools } from "./enrichment.js";
|
|
14
16
|
import { BulkTools } from "./bulk.js";
|
|
15
17
|
import { ExportTools } from "./exports.js";
|
|
@@ -36,7 +38,6 @@ import { CalendarTools } from "./calendar.js";
|
|
|
36
38
|
import { SplitSheetsTools } from "./split-sheets.js";
|
|
37
39
|
import { AgreementTypesTools } from "./agreement-types.js";
|
|
38
40
|
import { DashboardTools } from "./dashboard.js";
|
|
39
|
-
import { IntegrationsTools } from "./integrations.js";
|
|
40
41
|
import { SettingsTools } from "./settings.js";
|
|
41
42
|
import { FeedbackTools } from "./feedback.js";
|
|
42
43
|
import { StorageConfigTools } from "./storage-config.js";
|
|
@@ -44,9 +45,13 @@ import { SubscriptionTools } from "./subscription.js";
|
|
|
44
45
|
import { OnboardingTools } from "./onboarding.js";
|
|
45
46
|
import { ReportIssueTools } from "./report-issue.js";
|
|
46
47
|
import { MyReportedIssuesTools } from "./my-reported-issues.js";
|
|
48
|
+
import { MyRecentQuestionsTools } from "./my-recent-questions.js";
|
|
47
49
|
import { AgentIdentityTools } from "./agent-identity.js";
|
|
48
50
|
import { RoyaltiesTools } from "./royalties.js";
|
|
49
51
|
import { ShareLinksTools } from "./share-links.js";
|
|
52
|
+
import { SharingTools } from "./sharing.js";
|
|
53
|
+
import { ExplainabilityTools } from "./explainability.js";
|
|
54
|
+
import { AccessSimulateTools } from "./access-simulate.js";
|
|
50
55
|
import { DisputesTools } from "./disputes.js";
|
|
51
56
|
import { CustodyTools } from "./custody.js";
|
|
52
57
|
import { PurchasesTools } from "./purchases.js";
|
|
@@ -66,6 +71,46 @@ import { getToolMetadata } from "./metadata.js";
|
|
|
66
71
|
import { getRecoveryHint } from "./recovery-hints.js";
|
|
67
72
|
import { generateConfirmationToken, validateAndConsumeToken, } from "@withpica/mcp-utils";
|
|
68
73
|
import { buildSessionState, incrementSessionMutations, resetSinceLastBriefing, } from "@withpica/mcp-utils";
|
|
74
|
+
/**
|
|
75
|
+
* ADR-230 — derive the MCP-standard 3-value risk classification from the
|
|
76
|
+
* declared tier. Sourced once at registration and at audit-write time so
|
|
77
|
+
* the contract that hints, audit row, and confirmation requirement all
|
|
78
|
+
* agree is mechanical.
|
|
79
|
+
*/
|
|
80
|
+
export function tierToRiskLevel(tier) {
|
|
81
|
+
switch (tier) {
|
|
82
|
+
case "read":
|
|
83
|
+
return "safe";
|
|
84
|
+
case "draft":
|
|
85
|
+
case "write":
|
|
86
|
+
return "mutating";
|
|
87
|
+
case "destructive":
|
|
88
|
+
return "destructive";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* ADR-230 — derive the required API-key scope from the declared tier.
|
|
93
|
+
*
|
|
94
|
+
* - `read` → `read:<resource>` (write:* satisfies via `hasScope`).
|
|
95
|
+
* - `draft|write` → `write:<resource>`.
|
|
96
|
+
* - `destructive` → `destructive:*` (admin satisfies via `hasScope`); the
|
|
97
|
+
* write:<resource> scope is also required for the
|
|
98
|
+
* underlying write surface.
|
|
99
|
+
*
|
|
100
|
+
* Returns the *additional* scope tier beyond per-resource. Resource scope
|
|
101
|
+
* is composed at call-site by `lib/services/mcp-scopes.ts`.
|
|
102
|
+
*/
|
|
103
|
+
export function tierToScopeKind(tier) {
|
|
104
|
+
switch (tier) {
|
|
105
|
+
case "read":
|
|
106
|
+
return "read";
|
|
107
|
+
case "draft":
|
|
108
|
+
case "write":
|
|
109
|
+
return "write";
|
|
110
|
+
case "destructive":
|
|
111
|
+
return "destructive";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
69
114
|
/**
|
|
70
115
|
* Build a tool description with metadata injected.
|
|
71
116
|
* Format: "[category] original description"
|
|
@@ -85,6 +130,64 @@ export const CATEGORY_DISPLAY = {
|
|
|
85
130
|
comms: "send and share",
|
|
86
131
|
settings: "manage your account and team",
|
|
87
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* Sanitize tool parameters for `mcp_audit_log.parameters`. Pure function
|
|
135
|
+
* so it's directly testable; the registry's private method delegates here.
|
|
136
|
+
*
|
|
137
|
+
* Security audit 2026-05-11 P2. The audit log is queryable by anyone with
|
|
138
|
+
* team-portal access. Tools like `pica_sign_in`, `pica_share_links_*`,
|
|
139
|
+
* and `team_comms_send` previously landed bare emails / share tokens /
|
|
140
|
+
* OAuth refresh tokens in the log because the only redaction was a
|
|
141
|
+
* `delete sanitized.confirmation_token` + length truncation.
|
|
142
|
+
*
|
|
143
|
+
* Deny-list is keyed off the param name (case-insensitive substring
|
|
144
|
+
* match) — covers `password`, `api_key`, `token`, `secret`,
|
|
145
|
+
* `authorization`, plus `confirmation_token` via the `token` substring.
|
|
146
|
+
* Sister keys like `refresh_token` / `id_token` / `access_token` /
|
|
147
|
+
* `share_token` / `client_secret` also match.
|
|
148
|
+
*
|
|
149
|
+
* Email values (regardless of param key) get the local-part masked to
|
|
150
|
+
* the first two characters: `jane@example.com` → `ja***@example.com`.
|
|
151
|
+
* Locals ≤ 2 chars become all-asterisk.
|
|
152
|
+
*
|
|
153
|
+
* Strings > 500 chars are truncated to 500 + `...[truncated]`.
|
|
154
|
+
*
|
|
155
|
+
* Exported under `_internal` so test files can exercise the function
|
|
156
|
+
* without instantiating a `ToolRegistry`.
|
|
157
|
+
*/
|
|
158
|
+
function sanitizeAuditParams(args) {
|
|
159
|
+
const sanitized = { ...args };
|
|
160
|
+
const denyKeySubstrings = [
|
|
161
|
+
"password",
|
|
162
|
+
"api_key",
|
|
163
|
+
"token",
|
|
164
|
+
"secret",
|
|
165
|
+
"authorization",
|
|
166
|
+
];
|
|
167
|
+
for (const [key, value] of Object.entries(sanitized)) {
|
|
168
|
+
const lowerKey = key.toLowerCase();
|
|
169
|
+
if (denyKeySubstrings.some((needle) => lowerKey.includes(needle))) {
|
|
170
|
+
sanitized[key] = "[redacted]";
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (typeof value === "string" &&
|
|
174
|
+
/^[^\s@]{1,64}@[^\s@]{1,255}\.[^\s@]+$/.test(value)) {
|
|
175
|
+
const atIdx = value.indexOf("@");
|
|
176
|
+
const local = value.slice(0, atIdx);
|
|
177
|
+
const domain = value.slice(atIdx);
|
|
178
|
+
const maskedLocal = local.length <= 2
|
|
179
|
+
? "*".repeat(local.length)
|
|
180
|
+
: `${local.slice(0, 2)}***`;
|
|
181
|
+
sanitized[key] = `${maskedLocal}${domain}`;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (typeof value === "string" && value.length > 500) {
|
|
185
|
+
sanitized[key] = value.substring(0, 500) + "...[truncated]";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return sanitized;
|
|
189
|
+
}
|
|
190
|
+
export const _internal = { sanitizeAuditParams };
|
|
88
191
|
export class ToolRegistry {
|
|
89
192
|
tools;
|
|
90
193
|
pica;
|
|
@@ -111,6 +214,25 @@ export class ToolRegistry {
|
|
|
111
214
|
setCallerContext(context) {
|
|
112
215
|
this.callerContext = context;
|
|
113
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Read clientInfo from the MCP `initialize` handshake. SDK populates this
|
|
219
|
+
* on the per-request `Server` for HTTP and on the long-lived `Server` for
|
|
220
|
+
* stdio; either way getClientVersion() returns the same shape after the
|
|
221
|
+
* handshake completes. Returns empty when ctx is absent or the handshake
|
|
222
|
+
* hasn't run (lobby-mode dispatches that bypass the per-request transport).
|
|
223
|
+
*
|
|
224
|
+
* Stamped onto audit rows as provenance only — never used as a permission
|
|
225
|
+
* boundary, since clientInfo is self-declared by the client.
|
|
226
|
+
*/
|
|
227
|
+
extractClientInfo(ctx) {
|
|
228
|
+
const info = ctx?.server?.getClientVersion?.();
|
|
229
|
+
if (!info)
|
|
230
|
+
return {};
|
|
231
|
+
return {
|
|
232
|
+
client_name: typeof info.name === "string" ? info.name : undefined,
|
|
233
|
+
client_version: typeof info.version === "string" ? info.version : undefined,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
114
236
|
/**
|
|
115
237
|
* Register all available tools
|
|
116
238
|
*/
|
|
@@ -159,6 +281,11 @@ export class ToolRegistry {
|
|
|
159
281
|
groupsTools.getTools().forEach((tool) => {
|
|
160
282
|
this.tools.set(tool.definition.name, tool);
|
|
161
283
|
});
|
|
284
|
+
// Share-send tool (ADR-231)
|
|
285
|
+
const shareSendTools = new ShareSendTools(pica);
|
|
286
|
+
shareSendTools.getTools().forEach((tool) => {
|
|
287
|
+
this.tools.set(tool.definition.name, tool);
|
|
288
|
+
});
|
|
162
289
|
// Recordings tools
|
|
163
290
|
const recordingsTools = new RecordingsTools(pica);
|
|
164
291
|
recordingsTools.getTools().forEach((tool) => {
|
|
@@ -199,6 +326,11 @@ export class ToolRegistry {
|
|
|
199
326
|
memoryTools.getTools().forEach((tool) => {
|
|
200
327
|
this.tools.set(tool.definition.name, tool);
|
|
201
328
|
});
|
|
329
|
+
// Skills tools (ADR-140 Phase 2b — SEP-2640 bridge tool lane)
|
|
330
|
+
const skillsTools = new SkillsTools();
|
|
331
|
+
skillsTools.getTools().forEach((tool) => {
|
|
332
|
+
this.tools.set(tool.definition.name, tool);
|
|
333
|
+
});
|
|
202
334
|
// Enrichment tools
|
|
203
335
|
const enrichmentTools = new EnrichmentTools(pica);
|
|
204
336
|
enrichmentTools.getTools().forEach((tool) => {
|
|
@@ -330,11 +462,6 @@ export class ToolRegistry {
|
|
|
330
462
|
dashboardTools.getTools().forEach((tool) => {
|
|
331
463
|
this.tools.set(tool.definition.name, tool);
|
|
332
464
|
});
|
|
333
|
-
// Integrations tools (connection status)
|
|
334
|
-
const integrationsTools = new IntegrationsTools(pica);
|
|
335
|
-
integrationsTools.getTools().forEach((tool) => {
|
|
336
|
-
this.tools.set(tool.definition.name, tool);
|
|
337
|
-
});
|
|
338
465
|
// Settings tools (credits history, storage, org profile)
|
|
339
466
|
const settingsTools = new SettingsTools(pica);
|
|
340
467
|
settingsTools.getTools().forEach((tool) => {
|
|
@@ -377,6 +504,21 @@ export class ToolRegistry {
|
|
|
377
504
|
myReportedIssuesTools.getTools().forEach((tool) => {
|
|
378
505
|
this.tools.set(tool.definition.name, tool);
|
|
379
506
|
});
|
|
507
|
+
// My-recent-questions tool (ADR-226 Phase 5 — pica_my_recent_questions)
|
|
508
|
+
// — user-scoped read of the Phase 4 substrate (assistant_interactions
|
|
509
|
+
// joined with mcp_sessions for client identity). Atlas-resolved entry
|
|
510
|
+
// for "what did I ask the AI?".
|
|
511
|
+
//
|
|
512
|
+
// ADR-230 post-ship cleanup — the tier resolver is the registry's
|
|
513
|
+
// own `getToolTier` shape (declared `tier` on each tool definition).
|
|
514
|
+
// Lint Rule 13 makes tier required for every customer MCP tool, so
|
|
515
|
+
// this resolver returns a populated value for all known tools and
|
|
516
|
+
// `undefined` only for unknown / meta tools — `dominantTier` falls
|
|
517
|
+
// back to "read" in that case.
|
|
518
|
+
const myRecentQuestionsTools = new MyRecentQuestionsTools(pica, (name) => this.tools.get(name)?.definition.tier);
|
|
519
|
+
myRecentQuestionsTools.getTools().forEach((tool) => {
|
|
520
|
+
this.tools.set(tool.definition.name, tool);
|
|
521
|
+
});
|
|
380
522
|
// Agent identity tools (ADR-185 Part 1 — session-auth-only MCP surface).
|
|
381
523
|
// The three tools refuse grant-auth callers at the HTTP API layer;
|
|
382
524
|
// the stdio path carries an API key so behaviour is identical either
|
|
@@ -395,6 +537,21 @@ export class ToolRegistry {
|
|
|
395
537
|
shareLinksTools.getTools().forEach((tool) => {
|
|
396
538
|
this.tools.set(tool.definition.name, tool);
|
|
397
539
|
});
|
|
540
|
+
// Sharing trace tool (ADR-232 § Decision 2)
|
|
541
|
+
const sharingTools = new SharingTools(pica);
|
|
542
|
+
sharingTools.getTools().forEach((tool) => {
|
|
543
|
+
this.tools.set(tool.definition.name, tool);
|
|
544
|
+
});
|
|
545
|
+
// Explainability tools (ADR-232 § Decision 3)
|
|
546
|
+
const explainabilityTools = new ExplainabilityTools(pica);
|
|
547
|
+
explainabilityTools.getTools().forEach((tool) => {
|
|
548
|
+
this.tools.set(tool.definition.name, tool);
|
|
549
|
+
});
|
|
550
|
+
// Access simulator — read-only "who would see what?" preview.
|
|
551
|
+
const accessSimulateTools = new AccessSimulateTools(pica);
|
|
552
|
+
accessSimulateTools.getTools().forEach((tool) => {
|
|
553
|
+
this.tools.set(tool.definition.name, tool);
|
|
554
|
+
});
|
|
398
555
|
// Disputes tools (ADR-139 Step 3)
|
|
399
556
|
const disputesTools = new DisputesTools(pica);
|
|
400
557
|
disputesTools.getTools().forEach((tool) => {
|
|
@@ -456,72 +613,15 @@ export class ToolRegistry {
|
|
|
456
613
|
});
|
|
457
614
|
}
|
|
458
615
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
"_disconnect",
|
|
469
|
-
];
|
|
470
|
-
static MUTATING_PATTERNS = [
|
|
471
|
-
"_create",
|
|
472
|
-
"_update",
|
|
473
|
-
"_bulk_update",
|
|
474
|
-
"_invite",
|
|
475
|
-
"_link",
|
|
476
|
-
"_send_",
|
|
477
|
-
"_import_",
|
|
478
|
-
"_execute",
|
|
479
|
-
"_ingest",
|
|
480
|
-
"_enrich",
|
|
481
|
-
"_verify",
|
|
482
|
-
"_mark_",
|
|
483
|
-
"_review",
|
|
484
|
-
"_purchase",
|
|
485
|
-
"_submit",
|
|
486
|
-
"_generate",
|
|
487
|
-
"_set_default",
|
|
488
|
-
"_duplicate",
|
|
489
|
-
"_toggle",
|
|
490
|
-
"_save",
|
|
491
|
-
"_upload",
|
|
492
|
-
"_complete",
|
|
493
|
-
"_analyze",
|
|
494
|
-
"_analyse",
|
|
495
|
-
"_identify",
|
|
496
|
-
"_resend",
|
|
497
|
-
"_notify",
|
|
498
|
-
];
|
|
499
|
-
// Read-only tools that match mutating patterns by name but are actually safe
|
|
500
|
-
static SAFE_OVERRIDES = new Set([
|
|
501
|
-
"pica_collaborators_invites_list",
|
|
502
|
-
"pica_enrichment_candidates",
|
|
503
|
-
"pica_enrichment_compare",
|
|
504
|
-
"pica_find_duplicates",
|
|
505
|
-
"pica_import_analyze",
|
|
506
|
-
"pica_import_validate",
|
|
507
|
-
"pica_import_fields",
|
|
508
|
-
"pica_import_template",
|
|
509
|
-
"pica_import_documents_query",
|
|
510
|
-
"pica_import_documents_inspect",
|
|
511
|
-
"pica_send_query",
|
|
512
|
-
"pica_share_links_list",
|
|
513
|
-
]);
|
|
514
|
-
classifyTool(name) {
|
|
515
|
-
if (ToolRegistry.SAFE_OVERRIDES.has(name)) {
|
|
516
|
-
return "safe";
|
|
517
|
-
}
|
|
518
|
-
if (ToolRegistry.DESTRUCTIVE_PATTERNS.some((p) => name.includes(p))) {
|
|
519
|
-
return "destructive";
|
|
520
|
-
}
|
|
521
|
-
if (ToolRegistry.MUTATING_PATTERNS.some((p) => name.includes(p))) {
|
|
522
|
-
return "mutating";
|
|
523
|
-
}
|
|
524
|
-
return "safe";
|
|
616
|
+
/**
|
|
617
|
+
* ADR-230 — declared tier lookup by tool name. Used by the HTTP MCP
|
|
618
|
+
* dispatcher (`app/api/mcp/route.ts`) to enforce the elevated
|
|
619
|
+
* `destructive:*` scope on top of the resource-level scope from
|
|
620
|
+
* `lib/services/mcp-scopes`. Returns undefined for unknown tools or
|
|
621
|
+
* tools that haven't declared tier yet (legacy tolerance).
|
|
622
|
+
*/
|
|
623
|
+
getToolTier(name) {
|
|
624
|
+
return this.tools.get(name)?.definition.tier;
|
|
525
625
|
}
|
|
526
626
|
/**
|
|
527
627
|
* List all available tools with write-safety prefixes injected.
|
|
@@ -543,9 +643,14 @@ export class ToolRegistry {
|
|
|
543
643
|
return toolsToList.map((tool) => {
|
|
544
644
|
let definition = tool.definition;
|
|
545
645
|
const metadata = getToolMetadata(definition.name);
|
|
546
|
-
//
|
|
547
|
-
|
|
548
|
-
|
|
646
|
+
// ADR-230 — declared tier is the source of truth for hints + risk.
|
|
647
|
+
// Lint Rule 13 enforces tier presence on every customer MCP tool;
|
|
648
|
+
// metadata.risk is a quiet fallback for the unlikely case of a tool
|
|
649
|
+
// that bypasses lint.
|
|
650
|
+
const declaredTier = definition.tier;
|
|
651
|
+
const isDestructive = declaredTier
|
|
652
|
+
? declaredTier === "destructive"
|
|
653
|
+
: metadata?.risk === "destructive";
|
|
549
654
|
if (isDestructive) {
|
|
550
655
|
definition = {
|
|
551
656
|
...definition,
|
|
@@ -573,6 +678,26 @@ export class ToolRegistry {
|
|
|
573
678
|
},
|
|
574
679
|
};
|
|
575
680
|
}
|
|
681
|
+
// ADR-230 — derive hints from declared tier when present (every
|
|
682
|
+
// tool registered in mcp-server/src/tools/*.ts has tier as of
|
|
683
|
+
// PR-1; the metadata branch stays as a defensive code path).
|
|
684
|
+
if (declaredTier && metadata) {
|
|
685
|
+
return {
|
|
686
|
+
...definition,
|
|
687
|
+
description: injectMetadataIntoDescription(tool.definition.description, metadata),
|
|
688
|
+
annotations: {
|
|
689
|
+
title: metadata.display_name,
|
|
690
|
+
readOnlyHint: declaredTier === "read",
|
|
691
|
+
destructiveHint: declaredTier === "destructive",
|
|
692
|
+
idempotentHint: declaredTier !== "destructive" && metadata.retry_safe,
|
|
693
|
+
openWorldHint: false,
|
|
694
|
+
// ADR-199 extended annotation classes — read by ChatGPT/Claude.ai
|
|
695
|
+
// connectors directly. Derived from declared tier so the four
|
|
696
|
+
// surfaces (hint, riskLevel, audit row, scope) all agree.
|
|
697
|
+
...this.deriveExtendedAnnotations(definition, metadata, undefined, declaredTier),
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
}
|
|
576
701
|
if (metadata) {
|
|
577
702
|
return {
|
|
578
703
|
...definition,
|
|
@@ -583,25 +708,25 @@ export class ToolRegistry {
|
|
|
583
708
|
destructiveHint: metadata.risk === "destructive",
|
|
584
709
|
idempotentHint: metadata.risk !== "destructive" && metadata.retry_safe,
|
|
585
710
|
openWorldHint: false,
|
|
586
|
-
// ADR-199 extended annotation classes — read by ChatGPT/Claude.ai
|
|
587
|
-
// connectors directly. Derived from existing metadata so existing
|
|
588
|
-
// tools get this for free without per-tool edits.
|
|
589
711
|
...this.deriveExtendedAnnotations(definition, metadata),
|
|
590
712
|
},
|
|
591
713
|
};
|
|
592
714
|
}
|
|
593
|
-
//
|
|
594
|
-
|
|
715
|
+
// ADR-230 — tools without a TOOL_METADATA entry (the 5 lobby
|
|
716
|
+
// meta-tools — sign_in/out/discover/tool_details/execute) still
|
|
717
|
+
// declare tier. Source hints from declared tier; default to read
|
|
718
|
+
// when tier is somehow missing (lint blocks; defensive only).
|
|
719
|
+
const tierForHints = declaredTier ?? "read";
|
|
595
720
|
const annotations = {
|
|
596
721
|
title: definition.name,
|
|
597
|
-
readOnlyHint:
|
|
598
|
-
destructiveHint:
|
|
722
|
+
readOnlyHint: tierForHints === "read",
|
|
723
|
+
destructiveHint: tierForHints === "destructive",
|
|
599
724
|
idempotentHint: false,
|
|
600
725
|
openWorldHint: false,
|
|
601
|
-
// ADR-199: derive the extended classes
|
|
602
|
-
//
|
|
603
|
-
//
|
|
604
|
-
...this.deriveExtendedAnnotations(definition, null,
|
|
726
|
+
// ADR-199: derive the extended classes from declared tier so the
|
|
727
|
+
// category/risk surface stays aligned with the rest of the
|
|
728
|
+
// hint/audit/scope chain.
|
|
729
|
+
...this.deriveExtendedAnnotations(definition, null, undefined, tierForHints),
|
|
605
730
|
};
|
|
606
731
|
// No risk prefix needed — annotations handle this structurally
|
|
607
732
|
return { ...definition, annotations };
|
|
@@ -616,9 +741,16 @@ export class ToolRegistry {
|
|
|
616
741
|
* edits required for the retrofit. Future tool authors can override by
|
|
617
742
|
* setting fields directly on definition.annotations.
|
|
618
743
|
*/
|
|
619
|
-
deriveExtendedAnnotations(definition, metadata,
|
|
744
|
+
deriveExtendedAnnotations(definition, metadata, _classification, declaredTier) {
|
|
620
745
|
const name = definition.name;
|
|
621
|
-
|
|
746
|
+
// ADR-230 — declared tier is the source of truth. metadata.risk
|
|
747
|
+
// remains a quiet fallback for any tool that bypasses lint Rule 13.
|
|
748
|
+
// (The `_classification` parameter is retained as an unused
|
|
749
|
+
// positional slot so legacy call signatures continue to compile;
|
|
750
|
+
// its value is now ignored — classifyTool was removed in PR-3.)
|
|
751
|
+
const risk = declaredTier
|
|
752
|
+
? tierToRiskLevel(declaredTier)
|
|
753
|
+
: (metadata?.risk ?? "safe");
|
|
622
754
|
// Side-effecting external systems: telegram, send-hub, comms, broadcasts.
|
|
623
755
|
// Pattern-derived so new tools inherit without edits.
|
|
624
756
|
const externalSideEffectPatterns = [
|
|
@@ -638,10 +770,16 @@ export class ToolRegistry {
|
|
|
638
770
|
// the agent wants to confirm — the gap report flagged this as a useful
|
|
639
771
|
// class for ChatGPT to surface in the UI.
|
|
640
772
|
const requiresFollowUpInspection = risk === "mutating" || risk === "destructive";
|
|
773
|
+
// ADR-230 — destructive tier always requires confirmation. Other tiers
|
|
774
|
+
// never do via this annotation (per-tool opt-in for `write` tier is
|
|
775
|
+
// expressed through `previewMode: "two_step_token"`, not here).
|
|
776
|
+
const requiresConfirmation = declaredTier
|
|
777
|
+
? declaredTier === "destructive"
|
|
778
|
+
: risk === "destructive";
|
|
641
779
|
return {
|
|
642
780
|
riskLevel: risk,
|
|
643
781
|
category: metadata?.category,
|
|
644
|
-
requiresConfirmation
|
|
782
|
+
requiresConfirmation,
|
|
645
783
|
createsExternalSideEffects,
|
|
646
784
|
returnsPaginated,
|
|
647
785
|
requiresFollowUpInspection,
|
|
@@ -677,6 +815,15 @@ export class ToolRegistry {
|
|
|
677
815
|
display_name: displayName,
|
|
678
816
|
};
|
|
679
817
|
}
|
|
818
|
+
case "pica_works_bulk_move_to_performances": {
|
|
819
|
+
const count = args.ids?.length || 0;
|
|
820
|
+
return {
|
|
821
|
+
action: "bulk reclassify works → performances",
|
|
822
|
+
target: `${count} ${count === 1 ? "work" : "works"}`,
|
|
823
|
+
warning: `this creates ${count} new live_performance row(s) and removes the source work(s) from the catalog. for works with verified identifiers (ISRC/ISWC/MLC) the source is soft-deleted — credits and agreement links survive but are orphaned to the removed work; for unverified works the deletion is permanent. performance metadata is sparse by default and will need filling in.`,
|
|
824
|
+
display_name: displayName,
|
|
825
|
+
};
|
|
826
|
+
}
|
|
680
827
|
case "pica_people_delete": {
|
|
681
828
|
const person = await pica.people.get(args.id).catch(() => null);
|
|
682
829
|
const personName = person?.first_name
|
|
@@ -689,6 +836,15 @@ export class ToolRegistry {
|
|
|
689
836
|
display_name: displayName,
|
|
690
837
|
};
|
|
691
838
|
}
|
|
839
|
+
case "pica_people_bulk_delete": {
|
|
840
|
+
const count = args.ids?.length || 0;
|
|
841
|
+
return {
|
|
842
|
+
action: "bulk delete people",
|
|
843
|
+
target: `${count} ${count === 1 ? "person" : "people"}`,
|
|
844
|
+
warning: `this will soft-delete ${count} person record(s). credits and agreements remain linked to the removed records.`,
|
|
845
|
+
display_name: displayName,
|
|
846
|
+
};
|
|
847
|
+
}
|
|
692
848
|
case "pica_agreements_delete": {
|
|
693
849
|
const agreementResult = await pica.agreements
|
|
694
850
|
.get(args.id)
|
|
@@ -834,18 +990,11 @@ export class ToolRegistry {
|
|
|
834
990
|
}
|
|
835
991
|
}
|
|
836
992
|
/**
|
|
837
|
-
* Sanitize tool parameters for audit logging
|
|
838
|
-
*
|
|
993
|
+
* Sanitize tool parameters for audit logging — delegates to the
|
|
994
|
+
* pure-function impl below so it can be exercised directly in tests.
|
|
839
995
|
*/
|
|
840
996
|
sanitizeParams(args) {
|
|
841
|
-
|
|
842
|
-
delete sanitized.confirmation_token;
|
|
843
|
-
for (const [key, value] of Object.entries(sanitized)) {
|
|
844
|
-
if (typeof value === "string" && value.length > 500) {
|
|
845
|
-
sanitized[key] = value.substring(0, 500) + "...[truncated]";
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
return sanitized;
|
|
997
|
+
return sanitizeAuditParams(args);
|
|
849
998
|
}
|
|
850
999
|
/**
|
|
851
1000
|
* ADR-208 Primitive B — opaque per-org cache key.
|
|
@@ -1018,10 +1167,22 @@ export class ToolRegistry {
|
|
|
1018
1167
|
throw new ToolExecutionError(`Tool not found: ${name}`);
|
|
1019
1168
|
}
|
|
1020
1169
|
const metadata = getToolMetadata(name);
|
|
1170
|
+
// ADR-230 — declared tier is the source of truth for the freshness +
|
|
1171
|
+
// confirmation gates and the audit-log surfaces. Fall back to
|
|
1172
|
+
// metadata.risk only if the tool somehow lacks tier (lint blocks
|
|
1173
|
+
// missing tier on creator + team + directory MCPs as of PR-2).
|
|
1174
|
+
const declaredTier = tool.definition.tier;
|
|
1175
|
+
const fallbackRisk = metadata?.risk ?? "safe";
|
|
1176
|
+
const auditTier = declaredTier ??
|
|
1177
|
+
(fallbackRisk === "safe"
|
|
1178
|
+
? "read"
|
|
1179
|
+
: fallbackRisk === "destructive"
|
|
1180
|
+
? "destructive"
|
|
1181
|
+
: "write");
|
|
1182
|
+
const auditRiskLevel = tierToRiskLevel(auditTier);
|
|
1183
|
+
const isDestructive = auditTier === "destructive";
|
|
1021
1184
|
// ── Session freshness gate (ADR-151) ──
|
|
1022
|
-
if (
|
|
1023
|
-
this.config &&
|
|
1024
|
-
!this.config.lobbyMode) {
|
|
1185
|
+
if (isDestructive && this.config && !this.config.lobbyMode) {
|
|
1025
1186
|
const creds = readCredentials(this.config.credentialsPath);
|
|
1026
1187
|
if (creds?.authenticated_at) {
|
|
1027
1188
|
const authAge = Date.now() - new Date(creds.authenticated_at).getTime();
|
|
@@ -1039,12 +1200,15 @@ export class ToolRegistry {
|
|
|
1039
1200
|
}
|
|
1040
1201
|
}
|
|
1041
1202
|
}
|
|
1042
|
-
// ── Destructive confirmation gate ──
|
|
1043
|
-
if (
|
|
1203
|
+
// ── Destructive confirmation gate (ADR-230 — keyed on declared tier) ──
|
|
1204
|
+
if (isDestructive) {
|
|
1044
1205
|
const confirmationToken = args.confirmation_token;
|
|
1045
1206
|
if (!confirmationToken) {
|
|
1046
|
-
// First call — generate preview and return confirmation challenge
|
|
1047
|
-
|
|
1207
|
+
// First call — generate preview and return confirmation challenge.
|
|
1208
|
+
// ADR-230 PR-4 — token state lives in Postgres for HTTP MCP under
|
|
1209
|
+
// Fluid Compute (or in-memory for stdio); both paths via the
|
|
1210
|
+
// active ConfirmationStore singleton, so callers stay unchanged.
|
|
1211
|
+
const token = await generateConfirmationToken(name, args);
|
|
1048
1212
|
const preview = await this.buildDestructivePreview(name, args);
|
|
1049
1213
|
const response = {
|
|
1050
1214
|
content: [
|
|
@@ -1070,6 +1234,7 @@ export class ToolRegistry {
|
|
|
1070
1234
|
?.logToolExecution({
|
|
1071
1235
|
tool_name: name,
|
|
1072
1236
|
tool_category: metadata?.category || "unknown",
|
|
1237
|
+
tier: "destructive",
|
|
1073
1238
|
risk_level: "destructive",
|
|
1074
1239
|
parameters: this.sanitizeParams(args),
|
|
1075
1240
|
result_status: "confirmation_required",
|
|
@@ -1077,12 +1242,14 @@ export class ToolRegistry {
|
|
|
1077
1242
|
execution_time_ms: Date.now() - startTime,
|
|
1078
1243
|
caller_identity: this.callerContext.callerIdentity,
|
|
1079
1244
|
transport: this.callerContext.transport,
|
|
1245
|
+
...this.extractClientInfo(ctx),
|
|
1080
1246
|
})
|
|
1081
1247
|
.catch(() => { }); // Never block on audit
|
|
1082
1248
|
return response;
|
|
1083
1249
|
}
|
|
1084
|
-
// Second call — validate token
|
|
1085
|
-
|
|
1250
|
+
// Second call — validate token (Postgres-backed under Fluid Compute
|
|
1251
|
+
// since ADR-230 PR-4; the active store handles cross-instance state).
|
|
1252
|
+
const validation = await validateAndConsumeToken(confirmationToken, name, args);
|
|
1086
1253
|
if (!validation.valid) {
|
|
1087
1254
|
return {
|
|
1088
1255
|
content: [
|
|
@@ -1115,10 +1282,10 @@ export class ToolRegistry {
|
|
|
1115
1282
|
}
|
|
1116
1283
|
}
|
|
1117
1284
|
// ADR-208 Primitive B — ambient session-state on write-tool results.
|
|
1118
|
-
// Read tools (`
|
|
1119
|
-
//
|
|
1120
|
-
//
|
|
1121
|
-
const isWriteTool =
|
|
1285
|
+
// Read tools (`tier: "read"`) skip this entirely so the high-volume
|
|
1286
|
+
// read path stays cheap. `result.isError === true` also skips because
|
|
1287
|
+
// a failed mutation didn't change catalog state.
|
|
1288
|
+
const isWriteTool = auditRiskLevel !== "safe";
|
|
1122
1289
|
if (isWriteTool && result && !result.isError) {
|
|
1123
1290
|
await this.attachSessionState(result, name);
|
|
1124
1291
|
}
|
|
@@ -1144,12 +1311,14 @@ export class ToolRegistry {
|
|
|
1144
1311
|
?.logToolExecution({
|
|
1145
1312
|
tool_name: name,
|
|
1146
1313
|
tool_category: metadata?.category || "unknown",
|
|
1147
|
-
|
|
1314
|
+
tier: auditTier,
|
|
1315
|
+
risk_level: auditRiskLevel,
|
|
1148
1316
|
parameters: this.sanitizeParams(args),
|
|
1149
1317
|
result_status: "success",
|
|
1150
1318
|
execution_time_ms: Date.now() - startTime,
|
|
1151
1319
|
caller_identity: this.callerContext.callerIdentity,
|
|
1152
1320
|
transport: this.callerContext.transport,
|
|
1321
|
+
...this.extractClientInfo(ctx),
|
|
1153
1322
|
})
|
|
1154
1323
|
.catch(() => { }); // Never block on audit
|
|
1155
1324
|
return result;
|
|
@@ -1170,13 +1339,19 @@ export class ToolRegistry {
|
|
|
1170
1339
|
?.logToolExecution({
|
|
1171
1340
|
tool_name: name,
|
|
1172
1341
|
tool_category: metadata?.category || "unknown",
|
|
1173
|
-
|
|
1342
|
+
tier: auditTier,
|
|
1343
|
+
risk_level: auditRiskLevel,
|
|
1174
1344
|
parameters: this.sanitizeParams(args),
|
|
1175
1345
|
result_status: "error",
|
|
1176
1346
|
error_code: parsed.error,
|
|
1347
|
+
// Stage A — forward the parsed body's message so mcp_audit_log
|
|
1348
|
+
// captures the underlying cause. Without this every error row had
|
|
1349
|
+
// error_message=null, leaving UNKNOWN_ERROR rows opaque.
|
|
1350
|
+
error_message: typeof parsed.message === "string" ? parsed.message : undefined,
|
|
1177
1351
|
execution_time_ms: Date.now() - startTime,
|
|
1178
1352
|
caller_identity: this.callerContext.callerIdentity,
|
|
1179
1353
|
transport: this.callerContext.transport,
|
|
1354
|
+
...this.extractClientInfo(ctx),
|
|
1180
1355
|
})
|
|
1181
1356
|
.catch(() => { }); // Never block on audit
|
|
1182
1357
|
return {
|