@intutic/shared-types 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/dist/anomaly.d.ts +6 -10
  2. package/dist/anomaly.d.ts.map +1 -1
  3. package/dist/anomaly.js +0 -2
  4. package/dist/anomaly.js.map +1 -1
  5. package/dist/api-contracts.d.ts +1 -1
  6. package/dist/api-contracts.js +1 -1
  7. package/dist/attenuation.d.ts +1 -1
  8. package/dist/attenuation.js +1 -1
  9. package/dist/auth.d.ts +10 -9
  10. package/dist/auth.d.ts.map +1 -1
  11. package/dist/auth.js +3 -4
  12. package/dist/auth.js.map +1 -1
  13. package/dist/billing.d.ts +1 -1
  14. package/dist/billing.js +1 -1
  15. package/dist/destructiveSql.d.ts +48 -0
  16. package/dist/destructiveSql.d.ts.map +1 -0
  17. package/dist/destructiveSql.js +43 -0
  18. package/dist/destructiveSql.js.map +1 -0
  19. package/dist/devInventory.d.ts +120 -0
  20. package/dist/devInventory.d.ts.map +1 -0
  21. package/dist/devInventory.js +80 -0
  22. package/dist/devInventory.js.map +1 -0
  23. package/dist/enums.d.ts +18 -21
  24. package/dist/enums.d.ts.map +1 -1
  25. package/dist/enums.js +24 -28
  26. package/dist/enums.js.map +1 -1
  27. package/dist/errors.js +3 -3
  28. package/dist/errors.js.map +1 -1
  29. package/dist/finops.d.ts +14 -2
  30. package/dist/finops.d.ts.map +1 -1
  31. package/dist/finops.js +15 -1
  32. package/dist/finops.js.map +1 -1
  33. package/dist/gateDecisions.d.ts +18 -0
  34. package/dist/gateDecisions.d.ts.map +1 -0
  35. package/dist/gateDecisions.js +20 -0
  36. package/dist/gateDecisions.js.map +1 -0
  37. package/dist/gateIdentity.d.ts +23 -0
  38. package/dist/gateIdentity.d.ts.map +1 -0
  39. package/dist/gateIdentity.js +32 -0
  40. package/dist/gateIdentity.js.map +1 -0
  41. package/dist/gateKind.d.ts +172 -0
  42. package/dist/gateKind.d.ts.map +1 -0
  43. package/dist/gateKind.js +160 -0
  44. package/dist/gateKind.js.map +1 -0
  45. package/dist/gateLimits.d.ts +170 -0
  46. package/dist/gateLimits.d.ts.map +1 -0
  47. package/dist/gateLimits.js +96 -0
  48. package/dist/gateLimits.js.map +1 -0
  49. package/dist/gitRemote.d.ts +17 -0
  50. package/dist/gitRemote.d.ts.map +1 -0
  51. package/dist/gitRemote.js +71 -0
  52. package/dist/gitRemote.js.map +1 -0
  53. package/dist/governanceCoverage.d.ts +1 -1
  54. package/dist/governanceCoverage.js +1 -1
  55. package/dist/guardrailIr.d.ts +3 -3
  56. package/dist/guardrailIr.d.ts.map +1 -1
  57. package/dist/guardrailIr.js +3 -3
  58. package/dist/guardrailIr.js.map +1 -1
  59. package/dist/guardrailRender.d.ts +1 -1
  60. package/dist/guardrailRender.js +1 -1
  61. package/dist/harnessRules.d.ts +74 -0
  62. package/dist/harnessRules.d.ts.map +1 -0
  63. package/dist/harnessRules.js +142 -0
  64. package/dist/harnessRules.js.map +1 -0
  65. package/dist/holdMessages.d.ts +25 -0
  66. package/dist/holdMessages.d.ts.map +1 -0
  67. package/dist/holdMessages.js +34 -0
  68. package/dist/holdMessages.js.map +1 -0
  69. package/dist/identity.d.ts +1 -1
  70. package/dist/identity.js +2 -2
  71. package/dist/identity.js.map +1 -1
  72. package/dist/index.d.ts +33 -5
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +132 -10
  75. package/dist/index.js.map +1 -1
  76. package/dist/intelligence.d.ts +1 -1
  77. package/dist/mcpBudgets.d.ts +191 -0
  78. package/dist/mcpBudgets.d.ts.map +1 -0
  79. package/dist/mcpBudgets.js +144 -0
  80. package/dist/mcpBudgets.js.map +1 -0
  81. package/dist/mcpRegistryRecord.d.ts +143 -0
  82. package/dist/mcpRegistryRecord.d.ts.map +1 -0
  83. package/dist/mcpRegistryRecord.js +229 -0
  84. package/dist/mcpRegistryRecord.js.map +1 -0
  85. package/dist/mcpToolRisk.d.ts +82 -0
  86. package/dist/mcpToolRisk.d.ts.map +1 -0
  87. package/dist/mcpToolRisk.js +334 -0
  88. package/dist/mcpToolRisk.js.map +1 -0
  89. package/dist/modelCatalog.d.ts +2 -2
  90. package/dist/modelCatalog.generated.js +1 -1
  91. package/dist/modelCatalog.generated.js.map +1 -1
  92. package/dist/modelCatalog.js +2 -2
  93. package/dist/notifications.d.ts +8 -150
  94. package/dist/notifications.d.ts.map +1 -1
  95. package/dist/notifications.js +241 -1
  96. package/dist/notifications.js.map +1 -1
  97. package/dist/phrases.d.ts +87 -0
  98. package/dist/phrases.d.ts.map +1 -0
  99. package/dist/phrases.js +241 -0
  100. package/dist/phrases.js.map +1 -0
  101. package/dist/piiDetectors.d.ts +97 -0
  102. package/dist/piiDetectors.d.ts.map +1 -0
  103. package/dist/piiDetectors.js +96 -0
  104. package/dist/piiDetectors.js.map +1 -0
  105. package/dist/piiDetectors.json +71 -0
  106. package/dist/policy.d.ts +1 -1
  107. package/dist/policy.js +1 -1
  108. package/dist/policyGuardrails.d.ts +127 -10
  109. package/dist/policyGuardrails.d.ts.map +1 -1
  110. package/dist/policyGuardrails.js +59 -6
  111. package/dist/policyGuardrails.js.map +1 -1
  112. package/dist/posturePresets.js +1 -1
  113. package/dist/posturePresets.js.map +1 -1
  114. package/dist/providerVerification.d.ts +2 -2
  115. package/dist/providerVerification.js +2 -2
  116. package/dist/providers.d.ts +4 -4
  117. package/dist/providers.d.ts.map +1 -1
  118. package/dist/providers.js +11 -6
  119. package/dist/providers.js.map +1 -1
  120. package/dist/regoBuiltins.d.ts +42 -0
  121. package/dist/regoBuiltins.d.ts.map +1 -0
  122. package/dist/regoBuiltins.js +732 -0
  123. package/dist/regoBuiltins.js.map +1 -0
  124. package/dist/regoRules.d.ts +105 -0
  125. package/dist/regoRules.d.ts.map +1 -0
  126. package/dist/regoRules.js +407 -0
  127. package/dist/regoRules.js.map +1 -0
  128. package/dist/ruleLoadFailures.d.ts +15 -0
  129. package/dist/ruleLoadFailures.d.ts.map +1 -0
  130. package/dist/ruleLoadFailures.js +26 -0
  131. package/dist/ruleLoadFailures.js.map +1 -0
  132. package/dist/scriptScan.d.ts +5 -5
  133. package/dist/scriptScan.js +3 -3
  134. package/dist/sequence.d.ts +52 -0
  135. package/dist/sequence.d.ts.map +1 -0
  136. package/dist/sequence.js +127 -0
  137. package/dist/sequence.js.map +1 -0
  138. package/dist/session.d.ts +1 -1
  139. package/dist/session.js +1 -1
  140. package/dist/skillScan.d.ts +3 -3
  141. package/dist/skillScan.d.ts.map +1 -1
  142. package/dist/skillScan.js +26 -4
  143. package/dist/skillScan.js.map +1 -1
  144. package/dist/sop.d.ts +5 -5
  145. package/dist/sop.js +2 -2
  146. package/dist/sourceProviders.d.ts +1 -8
  147. package/dist/sourceProviders.d.ts.map +1 -1
  148. package/dist/sourceProviders.js +2 -9
  149. package/dist/sourceProviders.js.map +1 -1
  150. package/dist/ssoGroupClearance.d.ts +18 -2
  151. package/dist/ssoGroupClearance.d.ts.map +1 -1
  152. package/dist/ssoGroupClearance.js +43 -4
  153. package/dist/ssoGroupClearance.js.map +1 -1
  154. package/dist/sync.d.ts +0 -1
  155. package/dist/sync.d.ts.map +1 -1
  156. package/dist/sync.js +0 -1
  157. package/dist/sync.js.map +1 -1
  158. package/dist/taskManagement.d.ts +0 -2
  159. package/dist/taskManagement.d.ts.map +1 -1
  160. package/dist/taskManagement.js +0 -2
  161. package/dist/taskManagement.js.map +1 -1
  162. package/dist/toolPoison.d.ts +33 -0
  163. package/dist/toolPoison.d.ts.map +1 -0
  164. package/dist/toolPoison.js +85 -0
  165. package/dist/toolPoison.js.map +1 -0
  166. package/dist/usage.d.ts +186 -0
  167. package/dist/usage.d.ts.map +1 -1
  168. package/dist/wasmBinary.d.ts +25 -0
  169. package/dist/wasmBinary.d.ts.map +1 -0
  170. package/dist/wasmBinary.js +111 -0
  171. package/dist/wasmBinary.js.map +1 -0
  172. package/dist/wasmFuel.d.ts +52 -0
  173. package/dist/wasmFuel.d.ts.map +1 -0
  174. package/dist/wasmFuel.js +320 -0
  175. package/dist/wasmFuel.js.map +1 -0
  176. package/dist/wasmMemoryCap.d.ts +21 -0
  177. package/dist/wasmMemoryCap.d.ts.map +1 -0
  178. package/dist/wasmMemoryCap.js +58 -0
  179. package/dist/wasmMemoryCap.js.map +1 -0
  180. package/dist/workspaceSettings.d.ts +58 -17
  181. package/dist/workspaceSettings.d.ts.map +1 -1
  182. package/dist/workspaceSettings.js +2 -1
  183. package/dist/workspaceSettings.js.map +1 -1
  184. package/package.json +2 -2
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ /**
3
+ * The workspace's MCP server registry decisions, as the gates apply them.
4
+ *
5
+ * An owner or admin approves or blocks each MCP server on the MCP Servers
6
+ * page, disables tools within one, and sets what happens to a server nobody
7
+ * has decided on (`mcpDefaultPolicy`); a high-risk tool-set change can send a
8
+ * server back to the queue. The control plane serves those decisions as
9
+ * `mcpRegistry` on `GET /api/v1/policy/resolve`. The MCP proxy applies them to
10
+ * the servers it fronts; the sync daemon writes them into the policy snapshot
11
+ * as an `@mcp_registry` record so the harness hook gates apply them to every
12
+ * `mcp__<server>__<tool>` call, the servers no proxy fronts included.
13
+ *
14
+ * {@link evaluateMcpRegistry} is the one decision. The MCP proxy and the
15
+ * control plane's hook gate call it, the JavaScript hook gates embed its
16
+ * source ({@link MCP_REGISTRY_JS_SOURCE}), `@intutic/gate` carries a
17
+ * byte-identical copy of this file, and the bash gates and `intutic-clawde`
18
+ * run its Python transliteration (`intutic_clawde/gate/mcp_registry.py`);
19
+ * `fixtures/mcp-registry-vectors.json` holds all of them to the same codes,
20
+ * rule ids and reasons.
21
+ *
22
+ * The workspace's `mcpAllowedServers` list rides beside it as an
23
+ * `@mcp_allowlist` record ({@link evaluateMcpAllowlist}). Both records sit
24
+ * inside the snapshot's digest, and a gate that finds the snapshot unverified
25
+ * (its digest broken or missing, or another workspace's) trusts neither: it
26
+ * refuses every MCP call with {@link mcpSnapshotUnverifiedRefusal}, at `block`
27
+ * even in an observe-only workspace. An edit that widens either record, or
28
+ * deletes one, therefore admits nothing. A policy activates only when it
29
+ * verifies; the sync daemon restores the last snapshot it verified.
30
+ *
31
+ * Self-contained (no imports) so `@intutic/gate`, which has no workspace
32
+ * dependencies, can carry the copy.
33
+ *
34
+ * @module
35
+ */
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.MCP_ALLOWLIST_RECORD_TAG = exports.MCP_SNAPSHOT_UNVERIFIED_JS_SOURCE = exports.MCP_ALLOWLIST_JS_SOURCE = exports.MCP_REGISTRY_RECORD_TAG = exports.MCP_REGISTRY_JS_SOURCE = void 0;
38
+ exports.evaluateMcpRegistry = evaluateMcpRegistry;
39
+ exports.parseMcpRegistryRecord = parseMcpRegistryRecord;
40
+ exports.isUnrestrictedMcpRegistry = isUnrestrictedMcpRegistry;
41
+ exports.encodeMcpRegistryRecord = encodeMcpRegistryRecord;
42
+ exports.decodeMcpRegistryRecord = decodeMcpRegistryRecord;
43
+ exports.evaluateMcpAllowlist = evaluateMcpAllowlist;
44
+ exports.mcpSnapshotUnverifiedRefusal = mcpSnapshotUnverifiedRefusal;
45
+ exports.encodeMcpAllowlistRecord = encodeMcpAllowlistRecord;
46
+ exports.decodeMcpAllowlistRecord = decodeMcpAllowlistRecord;
47
+ /**
48
+ * Decides one MCP tool call against the registry: a blocked server, then a
49
+ * held one, then — under `deny` — one not approved, then a disabled tool.
50
+ * Null lets the call continue.
51
+ *
52
+ * Self-contained (no imports, no module state) because the JavaScript hook
53
+ * gates run it as emitted source.
54
+ */
55
+ function evaluateMcpRegistry(registry, serverName, toolName) {
56
+ if (registry.blockedServers.indexOf(serverName) !== -1) {
57
+ return {
58
+ code: 'SERVER_BLOCKED',
59
+ ruleId: 'mcp_registry.' + serverName,
60
+ reason: 'MCP server "' + serverName + '" is blocked in this workspace\'s MCP server registry. ' +
61
+ 'An owner or admin can change that on the MCP Servers page.',
62
+ };
63
+ }
64
+ if (registry.heldServers.indexOf(serverName) !== -1) {
65
+ return {
66
+ code: 'SERVER_HELD',
67
+ ruleId: 'mcp_registry.' + serverName,
68
+ reason: 'MCP server "' + serverName + '" changed its tools in a way scored high risk, and this workspace ' +
69
+ 'holds such a server until it is approved again. It is waiting in the approval queue on the MCP ' +
70
+ 'Servers page for an owner or admin.',
71
+ };
72
+ }
73
+ if (registry.defaultPolicy === 'deny' && registry.approvedServers.indexOf(serverName) === -1) {
74
+ return {
75
+ code: 'SERVER_NOT_APPROVED',
76
+ ruleId: 'mcpDefaultPolicy',
77
+ reason: 'MCP server "' + serverName + '" is not approved in this workspace\'s MCP server registry, ' +
78
+ 'and the workspace refuses unapproved servers (mcpDefaultPolicy: deny). It is waiting in ' +
79
+ 'the approval queue on the MCP Servers page for an owner or admin.',
80
+ };
81
+ }
82
+ const disabled = Object.prototype.hasOwnProperty.call(registry.disabledTools, serverName)
83
+ ? registry.disabledTools[serverName]
84
+ : undefined;
85
+ if (disabled && disabled.indexOf(toolName) !== -1) {
86
+ return {
87
+ code: 'TOOL_DISABLED',
88
+ ruleId: 'mcp_registry.' + serverName + '.' + toolName,
89
+ reason: 'Tool "' + toolName + '" is disabled on MCP server "' + serverName + '" in this workspace\'s ' +
90
+ 'MCP server registry. An owner or admin can re-enable it on the MCP Servers page.',
91
+ };
92
+ }
93
+ return null;
94
+ }
95
+ /** {@link evaluateMcpRegistry} as source, for the JavaScript hook gates. */
96
+ exports.MCP_REGISTRY_JS_SOURCE = evaluateMcpRegistry.toString();
97
+ function stringList(value) {
98
+ return Array.isArray(value) ? value.filter((v) => typeof v === 'string') : [];
99
+ }
100
+ function isPlainObject(value) {
101
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
102
+ }
103
+ /**
104
+ * Reads `mcpRegistry` as the control plane sends it. Null for anything that is
105
+ * not a registry object; wrong-typed lists read as empty, and any default but
106
+ * `deny` reads as `allow`, the control plane's own reading of the setting.
107
+ */
108
+ function parseMcpRegistryRecord(value) {
109
+ if (!isPlainObject(value))
110
+ return null;
111
+ const disabledTools = {};
112
+ if (isPlainObject(value['disabledTools'])) {
113
+ for (const [server, tools] of Object.entries(value['disabledTools'])) {
114
+ const list = stringList(tools);
115
+ if (list.length > 0)
116
+ disabledTools[server] = list;
117
+ }
118
+ }
119
+ return {
120
+ defaultPolicy: value['defaultPolicy'] === 'deny' ? 'deny' : 'allow',
121
+ approvedServers: stringList(value['approvedServers']),
122
+ blockedServers: stringList(value['blockedServers']),
123
+ heldServers: stringList(value['heldServers']),
124
+ disabledTools,
125
+ };
126
+ }
127
+ /** Whether a registry refuses nothing: no snapshot record is written for one. */
128
+ function isUnrestrictedMcpRegistry(registry) {
129
+ return (registry.defaultPolicy === 'allow' &&
130
+ registry.blockedServers.length === 0 &&
131
+ registry.heldServers.length === 0 &&
132
+ Object.keys(registry.disabledTools).length === 0);
133
+ }
134
+ /**
135
+ * First column of the record's line in `policy-snapshot.rules`. A data line,
136
+ * like `@sso_groups`, so the digest every gate recomputes covers it; two
137
+ * columns, so every rule parser (which needs six) skips it.
138
+ */
139
+ exports.MCP_REGISTRY_RECORD_TAG = '@mcp_registry';
140
+ function toBase64(text) {
141
+ let binary = '';
142
+ for (const byte of new TextEncoder().encode(text))
143
+ binary += String.fromCharCode(byte);
144
+ return btoa(binary);
145
+ }
146
+ function fromBase64(b64) {
147
+ return new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)));
148
+ }
149
+ /** The record as one `.rules` line: the tag, a tab, base64 of its JSON. */
150
+ function encodeMcpRegistryRecord(registry) {
151
+ return `${exports.MCP_REGISTRY_RECORD_TAG}\t${toBase64(JSON.stringify(registry))}`;
152
+ }
153
+ /** Reads a record line back; null for any other line or a damaged one. */
154
+ function decodeMcpRegistryRecord(line) {
155
+ const [tag, b64, ...rest] = line.split('\t');
156
+ if (tag !== exports.MCP_REGISTRY_RECORD_TAG || !b64 || rest.length > 0)
157
+ return null;
158
+ try {
159
+ return parseMcpRegistryRecord(JSON.parse(fromBase64(b64)));
160
+ }
161
+ catch {
162
+ return null;
163
+ }
164
+ }
165
+ /**
166
+ * Decides one MCP call's server against the allowlist. Null lets the call
167
+ * continue; a refusal under `shadow` is recorded, not enforced, by the caller.
168
+ *
169
+ * Self-contained, like {@link evaluateMcpRegistry}, because the JavaScript
170
+ * hook gates run it as emitted source.
171
+ */
172
+ function evaluateMcpAllowlist(allowlist, serverName) {
173
+ if (allowlist.servers.indexOf(serverName) !== -1)
174
+ return null;
175
+ return {
176
+ code: 'SERVER_NOT_ALLOWED',
177
+ ruleId: 'mcp_allowlist',
178
+ reason: 'MCP server "' + serverName + '" is not on the MCP server allowlist for this workspace',
179
+ };
180
+ }
181
+ /** {@link evaluateMcpAllowlist} as source, for the JavaScript hook gates. */
182
+ exports.MCP_ALLOWLIST_JS_SOURCE = evaluateMcpAllowlist.toString();
183
+ /**
184
+ * Refuses one MCP call because the policy snapshot failed its digest or
185
+ * workspace check. Neither the registry's approvals nor the allowlist's
186
+ * servers can be vouched for then, and a deleted record is indistinguishable
187
+ * from one the workspace never set, so the snapshot admits no MCP server at
188
+ * all. Every gate that reads the snapshot refuses with this, at `block`.
189
+ *
190
+ * Self-contained, like {@link evaluateMcpRegistry}, because the JavaScript
191
+ * hook gates run it as emitted source.
192
+ */
193
+ function mcpSnapshotUnverifiedRefusal(serverName) {
194
+ return {
195
+ code: 'POLICY_SNAPSHOT_UNVERIFIED',
196
+ ruleId: 'policy_snapshot',
197
+ reason: 'MCP server "' + serverName + '" is refused because the policy snapshot on this machine failed its ' +
198
+ 'integrity check, so its MCP server registry and allowlist admit no server until the sync daemon ' +
199
+ 'restores a verified snapshot',
200
+ };
201
+ }
202
+ /** {@link mcpSnapshotUnverifiedRefusal} as source, for the JavaScript hook gates. */
203
+ exports.MCP_SNAPSHOT_UNVERIFIED_JS_SOURCE = mcpSnapshotUnverifiedRefusal.toString();
204
+ /**
205
+ * First column of the allowlist's line in `policy-snapshot.rules`. A data
206
+ * line, so the digest covers it and a server added to the list by hand fails
207
+ * the check; three columns, so every rule parser skips it.
208
+ */
209
+ exports.MCP_ALLOWLIST_RECORD_TAG = '@mcp_allowlist';
210
+ /**
211
+ * The allowlist as one `.rules` line: the tag, the severity and the server
212
+ * names joined by commas, tab-separated. Plain text, so the bash gates read
213
+ * it with parameter expansion; the snapshot writer drops a name holding
214
+ * whitespace or a comma.
215
+ */
216
+ function encodeMcpAllowlistRecord(allowlist) {
217
+ return `${exports.MCP_ALLOWLIST_RECORD_TAG}\t${allowlist.severity}\t${allowlist.servers.join(',')}`;
218
+ }
219
+ /**
220
+ * Reads an allowlist line back; null for any other line. A severity other
221
+ * than `shadow` reads as `block`, so a damaged one refuses.
222
+ */
223
+ function decodeMcpAllowlistRecord(line) {
224
+ const [tag, severity, servers, ...rest] = line.split('\t');
225
+ if (tag !== exports.MCP_ALLOWLIST_RECORD_TAG || severity === undefined || servers === undefined || rest.length > 0)
226
+ return null;
227
+ return { severity: severity === 'shadow' ? 'shadow' : 'block', servers: servers.split(',').filter(Boolean) };
228
+ }
229
+ //# sourceMappingURL=mcpRegistryRecord.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpRegistryRecord.js","sourceRoot":"","sources":["../src/mcpRegistryRecord.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Risk score for a change to an MCP server's tool set.
3
+ *
4
+ * When a server the registry already knows declares a different tool set — a
5
+ * tool added or removed, a description or input schema changed — the control
6
+ * plane scores the change with {@link scoreToolSetChange} and stores the score
7
+ * and its reasons on the registry's change record
8
+ * (`services/control-plane/src/services/mcpRegistryService.ts`).
9
+ *
10
+ * Rules only, no model: the same two tool sets always produce the same score
11
+ * and the same reasons, in the same order, so a score can be reproduced from
12
+ * the stored definitions and argued with. Each rule adds points:
13
+ *
14
+ * | rule | points | when |
15
+ * |------------------------|--------|--------------------------------------------------------------|
16
+ * | `description_poisoned` | 60 | a new or changed description matches a poisoning pattern it did not match before |
17
+ * | `new_tool_capability` | 20–40 | a new tool's name or description implies write, network, exec or credential access (per capability) |
18
+ * | `capability_gained` | 20–40 | a changed tool implies a capability it did not before (per capability) |
19
+ * | `confirmation_removed` | 35 | a changed tool lost a confirmation or dry-run argument, or stopped requiring or defaulting it |
20
+ * | `schema_widened` | 15 | a changed tool accepts more than before: new arguments, fewer required ones, lifted enums, patterns or limits |
21
+ * | `tool_added` | 5 | a new tool none of the rules above flag |
22
+ * | `description_changed` | 5 | a changed description none of the rules above flag |
23
+ *
24
+ * Capability points: exec 40, credential 40, network 25, write 20. A removed
25
+ * tool adds nothing: it narrows what an agent can do. The score is the sum,
26
+ * capped at 100; 50 or more is `high`, 20 or more `medium`, anything above 0
27
+ * `low`.
28
+ *
29
+ * @module
30
+ */
31
+ /** One tool as a server declares it in `tools/list`. */
32
+ export interface McpToolDefinition {
33
+ name: string;
34
+ description?: string;
35
+ inputSchema?: unknown;
36
+ }
37
+ /**
38
+ * How much of a definition the registry keeps. A longer description is cut
39
+ * here (its scanned prefix is what the risk score sees); a larger input
40
+ * schema is left out. Real servers' tools sit far below both.
41
+ */
42
+ export declare const MAX_TOOL_DESCRIPTION_CHARS = 8192;
43
+ export declare const MAX_TOOL_SCHEMA_CHARS = 16384;
44
+ /**
45
+ * One `tools/list` entry as the registry stores it: the name, the description
46
+ * cut to {@link MAX_TOOL_DESCRIPTION_CHARS}, and the input schema when it is an
47
+ * object no larger than {@link MAX_TOOL_SCHEMA_CHARS} serialised. `null` for an
48
+ * entry without a name.
49
+ */
50
+ export declare function normalizeToolDefinition(raw: unknown): McpToolDefinition | null;
51
+ export type McpToolCapability = 'exec' | 'credential' | 'network' | 'write';
52
+ export type McpToolRiskRule = 'description_poisoned' | 'new_tool_capability' | 'capability_gained' | 'confirmation_removed' | 'schema_widened' | 'tool_added' | 'description_changed';
53
+ export type McpToolRiskLevel = 'none' | 'low' | 'medium' | 'high';
54
+ export interface McpToolRiskReason {
55
+ rule: McpToolRiskRule;
56
+ tool: string;
57
+ points: number;
58
+ /** What the rule saw, in words. */
59
+ detail: string;
60
+ }
61
+ export interface McpToolSetChange {
62
+ added: string[];
63
+ removed: string[];
64
+ /** Tools present before and after whose description or input schema differs. */
65
+ changed: string[];
66
+ /** 0–100. */
67
+ score: number;
68
+ level: McpToolRiskLevel;
69
+ /** Sorted by tool, then rule. */
70
+ reasons: McpToolRiskReason[];
71
+ }
72
+ export declare const MCP_TOOL_RISK_HIGH = 50;
73
+ export declare const MCP_TOOL_RISK_MEDIUM = 20;
74
+ export declare const MCP_TOOL_CAPABILITY_POINTS: Readonly<Record<McpToolCapability, number>>;
75
+ export declare function riskLevelOf(score: number): McpToolRiskLevel;
76
+ /**
77
+ * Compares the tool set a server declared before with the one it declares
78
+ * now. Tools are matched by name; order and duplicate names (the first wins)
79
+ * do not affect the result.
80
+ */
81
+ export declare function scoreToolSetChange(previous: readonly McpToolDefinition[], next: readonly McpToolDefinition[]): McpToolSetChange;
82
+ //# sourceMappingURL=mcpToolRisk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpToolRisk.d.ts","sourceRoot":"","sources":["../src/mcpToolRisk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,OAAQ,CAAA;AAC/C,eAAO,MAAM,qBAAqB,QAAS,CAAA;AAE3C;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,GAAG,IAAI,CAY9E;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAA;AAE3E,MAAM,MAAM,eAAe,GACvB,sBAAsB,GACtB,qBAAqB,GACrB,mBAAmB,GACnB,sBAAsB,GACtB,gBAAgB,GAChB,YAAY,GACZ,qBAAqB,CAAA;AAEzB,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEjE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,gFAAgF;IAChF,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,aAAa;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,gBAAgB,CAAA;IACvB,iCAAiC;IACjC,OAAO,EAAE,iBAAiB,EAAE,CAAA;CAC7B;AAED,eAAO,MAAM,kBAAkB,KAAK,CAAA;AACpC,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAKlF,CAAA;AA4LD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAI3D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,EACtC,IAAI,EAAE,SAAS,iBAAiB,EAAE,GACjC,gBAAgB,CAmElB"}
@@ -0,0 +1,334 @@
1
+ "use strict";
2
+ /**
3
+ * Risk score for a change to an MCP server's tool set.
4
+ *
5
+ * When a server the registry already knows declares a different tool set — a
6
+ * tool added or removed, a description or input schema changed — the control
7
+ * plane scores the change with {@link scoreToolSetChange} and stores the score
8
+ * and its reasons on the registry's change record
9
+ * (`services/control-plane/src/services/mcpRegistryService.ts`).
10
+ *
11
+ * Rules only, no model: the same two tool sets always produce the same score
12
+ * and the same reasons, in the same order, so a score can be reproduced from
13
+ * the stored definitions and argued with. Each rule adds points:
14
+ *
15
+ * | rule | points | when |
16
+ * |------------------------|--------|--------------------------------------------------------------|
17
+ * | `description_poisoned` | 60 | a new or changed description matches a poisoning pattern it did not match before |
18
+ * | `new_tool_capability` | 20–40 | a new tool's name or description implies write, network, exec or credential access (per capability) |
19
+ * | `capability_gained` | 20–40 | a changed tool implies a capability it did not before (per capability) |
20
+ * | `confirmation_removed` | 35 | a changed tool lost a confirmation or dry-run argument, or stopped requiring or defaulting it |
21
+ * | `schema_widened` | 15 | a changed tool accepts more than before: new arguments, fewer required ones, lifted enums, patterns or limits |
22
+ * | `tool_added` | 5 | a new tool none of the rules above flag |
23
+ * | `description_changed` | 5 | a changed description none of the rules above flag |
24
+ *
25
+ * Capability points: exec 40, credential 40, network 25, write 20. A removed
26
+ * tool adds nothing: it narrows what an agent can do. The score is the sum,
27
+ * capped at 100; 50 or more is `high`, 20 or more `medium`, anything above 0
28
+ * `low`.
29
+ *
30
+ * @module
31
+ */
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.MCP_TOOL_CAPABILITY_POINTS = exports.MCP_TOOL_RISK_MEDIUM = exports.MCP_TOOL_RISK_HIGH = exports.MAX_TOOL_SCHEMA_CHARS = exports.MAX_TOOL_DESCRIPTION_CHARS = void 0;
34
+ exports.normalizeToolDefinition = normalizeToolDefinition;
35
+ exports.riskLevelOf = riskLevelOf;
36
+ exports.scoreToolSetChange = scoreToolSetChange;
37
+ const toolPoison_js_1 = require("./toolPoison.js");
38
+ /**
39
+ * How much of a definition the registry keeps. A longer description is cut
40
+ * here (its scanned prefix is what the risk score sees); a larger input
41
+ * schema is left out. Real servers' tools sit far below both.
42
+ */
43
+ exports.MAX_TOOL_DESCRIPTION_CHARS = 8_192;
44
+ exports.MAX_TOOL_SCHEMA_CHARS = 16_384;
45
+ /**
46
+ * One `tools/list` entry as the registry stores it: the name, the description
47
+ * cut to {@link MAX_TOOL_DESCRIPTION_CHARS}, and the input schema when it is an
48
+ * object no larger than {@link MAX_TOOL_SCHEMA_CHARS} serialised. `null` for an
49
+ * entry without a name.
50
+ */
51
+ function normalizeToolDefinition(raw) {
52
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw))
53
+ return null;
54
+ const entry = raw;
55
+ const name = entry['name'];
56
+ if (typeof name !== 'string' || name.length === 0 || name.length > 256)
57
+ return null;
58
+ const def = { name };
59
+ if (typeof entry['description'] === 'string')
60
+ def.description = entry['description'].slice(0, exports.MAX_TOOL_DESCRIPTION_CHARS);
61
+ const schema = entry['inputSchema'];
62
+ if (schema !== null && typeof schema === 'object' && !Array.isArray(schema) && JSON.stringify(schema).length <= exports.MAX_TOOL_SCHEMA_CHARS) {
63
+ def.inputSchema = schema;
64
+ }
65
+ return def;
66
+ }
67
+ exports.MCP_TOOL_RISK_HIGH = 50;
68
+ exports.MCP_TOOL_RISK_MEDIUM = 20;
69
+ exports.MCP_TOOL_CAPABILITY_POINTS = {
70
+ exec: 40,
71
+ credential: 40,
72
+ network: 25,
73
+ write: 20,
74
+ };
75
+ const RULE_POINTS = {
76
+ description_poisoned: 60,
77
+ confirmation_removed: 35,
78
+ schema_widened: 15,
79
+ tool_added: 5,
80
+ description_changed: 5,
81
+ };
82
+ const CAPABILITIES = ['exec', 'credential', 'network', 'write'];
83
+ /** Words in a tool's name that name a capability. Matched against whole name tokens. */
84
+ const NAME_TOKENS = {
85
+ exec: new Set(['exec', 'execute', 'run', 'shell', 'bash', 'sh', 'cmd', 'command', 'commands', 'spawn', 'eval', 'script', 'terminal', 'subprocess']),
86
+ credential: new Set(['credential', 'credentials', 'secret', 'secrets', 'password', 'passwords', 'passwd', 'token', 'tokens', 'apikey', 'auth', 'oauth', 'login', 'keychain', 'vault', 'ssh']),
87
+ network: new Set(['http', 'https', 'url', 'fetch', 'request', 'webhook', 'download', 'upload', 'curl', 'wget', 'browse', 'navigate', 'email', 'mail', 'smtp', 'socket', 'send']),
88
+ write: new Set(['write', 'delete', 'remove', 'rm', 'update', 'create', 'modify', 'edit', 'insert', 'drop', 'truncate', 'overwrite', 'move', 'rename', 'push', 'commit', 'deploy', 'publish', 'put', 'patch', 'upsert', 'append', 'merge', 'set']),
89
+ };
90
+ /**
91
+ * Phrases in a description that name a capability. Narrower than the name
92
+ * tokens on purpose: descriptions are prose, where "update" or "token" turn up
93
+ * in tools that neither write nor touch credentials.
94
+ */
95
+ const DESCRIPTION_PATTERNS = {
96
+ exec: /\b(execut(e|es|ing)\b|runs? (an? |any |arbitrary )?(shell |system |terminal )?commands?\b|shell commands?\b|spawn(s|ing)? (a |new )?process|arbitrary code\b)/i,
97
+ credential: /\b(read|reads|return|returns|get|gets|list|lists|retriev\w*|fetch\w*|export\w*|dump\w*|access\w*)\b[^.]{0,40}\b(credentials?|passwords?|secrets?|api[ _-]?keys?|access tokens?|private keys?|ssh keys?)\b/i,
98
+ network: /(https?:\/\/|\bhttp requests?\b|\bfetch(es)? (a |the |any )?(url|web ?page)s?\b|\bwebhooks?\b|\bdownloads?\b|\buploads?\b|\bsends? (an? )?(email|e-mail|message|request)s?\b)/i,
99
+ write: /\b(write|writes|delete|deletes|remove|removes|overwrite|overwrites|modif(y|ies)|truncate|truncates|drop|drops)\b[^.]{0,40}\b(files?|director(y|ies)|records?|rows?|tables?|database|repositor(y|ies)|branch(es)?|data)\b/i,
100
+ };
101
+ /** Argument names that ask for confirmation or a dry run, compared lowercased with `_` and `-` removed. */
102
+ const CONFIRMATION_ARGS = new Set([
103
+ 'confirm', 'confirmed', 'confirmation', 'requireconfirmation', 'dryrun', 'preview', 'approval', 'approve', 'approved', 'safemode',
104
+ ]);
105
+ function nameTokens(name) {
106
+ return name
107
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
108
+ .toLowerCase()
109
+ .split(/[^a-z0-9]+/)
110
+ .filter(Boolean);
111
+ }
112
+ function capabilitiesOf(tool) {
113
+ const tokens = nameTokens(tool.name);
114
+ const found = new Set();
115
+ for (const cap of CAPABILITIES) {
116
+ if (tokens.some((t) => NAME_TOKENS[cap].has(t)))
117
+ found.add(cap);
118
+ else if (tool.description && DESCRIPTION_PATTERNS[cap].test(tool.description))
119
+ found.add(cap);
120
+ }
121
+ return found;
122
+ }
123
+ function isConfirmationArg(name) {
124
+ return CONFIRMATION_ARGS.has(name.toLowerCase().replace(/[_-]/g, ''));
125
+ }
126
+ /** JSON with object keys sorted, so equal schemas compare equal whatever their key order. */
127
+ function stableJson(value) {
128
+ if (Array.isArray(value))
129
+ return `[${value.map(stableJson).join(',')}]`;
130
+ if (value !== null && typeof value === 'object') {
131
+ const obj = value;
132
+ return `{${Object.keys(obj).sort().map((k) => `${JSON.stringify(k)}:${stableJson(obj[k])}`).join(',')}}`;
133
+ }
134
+ return JSON.stringify(value) ?? 'null';
135
+ }
136
+ function asSchema(value) {
137
+ return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : null;
138
+ }
139
+ function propertiesOf(schema) {
140
+ return asSchema(schema['properties']) ?? {};
141
+ }
142
+ function requiredOf(schema) {
143
+ return Array.isArray(schema['required']) ? schema['required'].filter((r) => typeof r === 'string') : [];
144
+ }
145
+ function typesOf(schema) {
146
+ const t = schema['type'];
147
+ if (typeof t === 'string')
148
+ return [t];
149
+ if (Array.isArray(t))
150
+ return t.filter((x) => typeof x === 'string');
151
+ return null;
152
+ }
153
+ /** Upper bounds whose removal or increase accepts more input. */
154
+ const UPPER_BOUNDS = ['maxLength', 'maximum', 'exclusiveMaximum', 'maxItems', 'maxProperties'];
155
+ /** Lower bounds whose removal or decrease accepts more input. */
156
+ const LOWER_BOUNDS = ['minLength', 'minimum', 'exclusiveMinimum', 'minItems', 'minProperties'];
157
+ /**
158
+ * Everything `next` accepts that `prev` did not, as phrases — and, separately,
159
+ * the confirmation arguments `next` weakened. Recurses into nested object
160
+ * properties and array items; `at` is the argument path for the phrases.
161
+ */
162
+ function compareSchemas(prev, next, at, widened, confirmations) {
163
+ const label = (name) => (at ? `${at}.${name}` : name);
164
+ const here = at ? `\`${at}\`` : 'the input';
165
+ const prevTypes = typesOf(prev);
166
+ const nextTypes = typesOf(next);
167
+ if (prevTypes && (!nextTypes || nextTypes.some((t) => !prevTypes.includes(t)))) {
168
+ widened.push(`${here} accepts ${nextTypes ? nextTypes.filter((t) => !prevTypes.includes(t)).join(', ') : 'any type'} now`);
169
+ }
170
+ if ('enum' in prev) {
171
+ const before = Array.isArray(prev['enum']) ? prev['enum'].map(stableJson) : [];
172
+ const after = Array.isArray(next['enum']) ? next['enum'].map(stableJson) : null;
173
+ if (!after)
174
+ widened.push(`${here} lost its list of allowed values`);
175
+ else if (after.some((v) => !before.includes(v)))
176
+ widened.push(`${here} allows new values`);
177
+ }
178
+ if ('const' in prev && stableJson(prev['const']) !== stableJson(next['const'])) {
179
+ widened.push(`${here} is no longer fixed to ${stableJson(prev['const'])}`);
180
+ }
181
+ if (typeof prev['pattern'] === 'string' && prev['pattern'] !== next['pattern']) {
182
+ widened.push(`${here} ${typeof next['pattern'] === 'string' ? 'has a different' : 'lost its'} pattern`);
183
+ }
184
+ for (const bound of UPPER_BOUNDS) {
185
+ const b = prev[bound];
186
+ if (typeof b === 'number' && !(typeof next[bound] === 'number' && next[bound] <= b)) {
187
+ widened.push(`${here} ${typeof next[bound] === 'number' ? 'raised' : 'dropped'} its ${bound}`);
188
+ }
189
+ }
190
+ for (const bound of LOWER_BOUNDS) {
191
+ const b = prev[bound];
192
+ if (typeof b === 'number' && !(typeof next[bound] === 'number' && next[bound] >= b)) {
193
+ widened.push(`${here} ${typeof next[bound] === 'number' ? 'lowered' : 'dropped'} its ${bound}`);
194
+ }
195
+ }
196
+ if (prev['additionalProperties'] === false && next['additionalProperties'] !== false) {
197
+ widened.push(`${here} accepts arguments it does not declare`);
198
+ }
199
+ const prevProps = propertiesOf(prev);
200
+ const nextProps = propertiesOf(next);
201
+ const prevRequired = requiredOf(prev);
202
+ const nextRequired = new Set(requiredOf(next));
203
+ for (const name of Object.keys(nextProps).sort()) {
204
+ if (!(name in prevProps))
205
+ widened.push(`new argument \`${label(name)}\``);
206
+ }
207
+ for (const name of Object.keys(prevProps).sort()) {
208
+ const confirmation = isConfirmationArg(name);
209
+ if (!(name in nextProps)) {
210
+ if (confirmation)
211
+ confirmations.push(`\`${label(name)}\` was removed`);
212
+ continue;
213
+ }
214
+ const prevProp = asSchema(prevProps[name]);
215
+ const nextProp = asSchema(nextProps[name]);
216
+ if (prevRequired.includes(name) && !nextRequired.has(name)) {
217
+ if (confirmation)
218
+ confirmations.push(`\`${label(name)}\` is no longer required`);
219
+ else
220
+ widened.push(`\`${label(name)}\` is no longer required`);
221
+ }
222
+ if (confirmation && prevProp?.['default'] === true && nextProp?.['default'] !== true) {
223
+ confirmations.push(`\`${label(name)}\` no longer defaults to true`);
224
+ }
225
+ if (prevProp && nextProp)
226
+ compareSchemas(prevProp, nextProp, label(name), widened, confirmations);
227
+ else if (prevProp && !nextProp)
228
+ widened.push(`\`${label(name)}\` lost its schema`);
229
+ }
230
+ const prevItems = asSchema(prev['items']);
231
+ const nextItems = asSchema(next['items']);
232
+ if (prevItems && nextItems)
233
+ compareSchemas(prevItems, nextItems, at ? `${at}[]` : '[]', widened, confirmations);
234
+ else if (prevItems && !nextItems)
235
+ widened.push(`${here} lost its item schema`);
236
+ }
237
+ function capabilityList(caps) {
238
+ const set = new Set(caps);
239
+ return CAPABILITIES.filter((c) => set.has(c));
240
+ }
241
+ const CAPABILITY_WORDS = {
242
+ exec: 'command execution',
243
+ credential: 'credential access',
244
+ network: 'network access',
245
+ write: 'write access',
246
+ };
247
+ function indexByName(tools) {
248
+ const map = new Map();
249
+ for (const t of tools)
250
+ if (typeof t.name === 'string' && t.name && !map.has(t.name))
251
+ map.set(t.name, t);
252
+ return map;
253
+ }
254
+ function riskLevelOf(score) {
255
+ if (score >= exports.MCP_TOOL_RISK_HIGH)
256
+ return 'high';
257
+ if (score >= exports.MCP_TOOL_RISK_MEDIUM)
258
+ return 'medium';
259
+ return score > 0 ? 'low' : 'none';
260
+ }
261
+ /**
262
+ * Compares the tool set a server declared before with the one it declares
263
+ * now. Tools are matched by name; order and duplicate names (the first wins)
264
+ * do not affect the result.
265
+ */
266
+ function scoreToolSetChange(previous, next) {
267
+ const before = indexByName(previous);
268
+ const after = indexByName(next);
269
+ const added = [...after.keys()].filter((n) => !before.has(n)).sort();
270
+ const removed = [...before.keys()].filter((n) => !after.has(n)).sort();
271
+ const changed = [];
272
+ const reasons = [];
273
+ const reason = (rule, tool, points, detail) => reasons.push({ rule, tool, points, detail });
274
+ for (const name of added) {
275
+ const tool = after.get(name);
276
+ const start = reasons.length;
277
+ const poison = tool.description ? (0, toolPoison_js_1.scanToolDescription)(tool.description) : [];
278
+ if (poison.length > 0) {
279
+ reason('description_poisoned', name, RULE_POINTS.description_poisoned, `new tool's description matches ${poison.join(', ')}`);
280
+ }
281
+ for (const cap of capabilityList(capabilitiesOf(tool))) {
282
+ reason('new_tool_capability', name, exports.MCP_TOOL_CAPABILITY_POINTS[cap], `new tool implies ${CAPABILITY_WORDS[cap]}`);
283
+ }
284
+ if (reasons.length === start)
285
+ reason('tool_added', name, RULE_POINTS.tool_added, 'new tool');
286
+ }
287
+ for (const name of [...after.keys()].filter((n) => before.has(n)).sort()) {
288
+ const prev = before.get(name);
289
+ const cur = after.get(name);
290
+ const descriptionChanged = (prev.description ?? '') !== (cur.description ?? '');
291
+ const schemaChanged = stableJson(prev.inputSchema ?? null) !== stableJson(cur.inputSchema ?? null);
292
+ if (!descriptionChanged && !schemaChanged)
293
+ continue;
294
+ changed.push(name);
295
+ const start = reasons.length;
296
+ if (descriptionChanged && cur.description) {
297
+ const was = new Set(prev.description ? (0, toolPoison_js_1.scanToolDescription)(prev.description) : []);
298
+ const now = (0, toolPoison_js_1.scanToolDescription)(cur.description).filter((p) => !was.has(p));
299
+ if (now.length > 0) {
300
+ reason('description_poisoned', name, RULE_POINTS.description_poisoned, `description now matches ${now.join(', ')}`);
301
+ }
302
+ }
303
+ const hadCaps = capabilitiesOf(prev);
304
+ for (const cap of capabilityList(capabilitiesOf(cur))) {
305
+ if (!hadCaps.has(cap)) {
306
+ reason('capability_gained', name, exports.MCP_TOOL_CAPABILITY_POINTS[cap], `now implies ${CAPABILITY_WORDS[cap]}`);
307
+ }
308
+ }
309
+ if (schemaChanged) {
310
+ const widened = [];
311
+ const confirmations = [];
312
+ compareSchemas(asSchema(prev.inputSchema) ?? {}, asSchema(cur.inputSchema) ?? {}, '', widened, confirmations);
313
+ if (confirmations.length > 0) {
314
+ reason('confirmation_removed', name, RULE_POINTS.confirmation_removed, confirmations.join('; '));
315
+ }
316
+ if (widened.length > 0)
317
+ reason('schema_widened', name, RULE_POINTS.schema_widened, widened.join('; '));
318
+ }
319
+ if (reasons.length === start && descriptionChanged) {
320
+ reason('description_changed', name, RULE_POINTS.description_changed, 'description changed');
321
+ }
322
+ }
323
+ const order = (r) => [r.tool, r.rule, r.detail];
324
+ reasons.sort((a, b) => {
325
+ const [x, y] = [order(a), order(b)];
326
+ for (let i = 0; i < x.length; i++)
327
+ if (x[i] !== y[i])
328
+ return x[i] < y[i] ? -1 : 1;
329
+ return 0;
330
+ });
331
+ const score = Math.min(100, reasons.reduce((sum, r) => sum + r.points, 0));
332
+ return { added, removed, changed: changed.sort(), score, level: riskLevelOf(score), reasons };
333
+ }
334
+ //# sourceMappingURL=mcpToolRisk.js.map