MCPcopy
hub / github.com/codeaashu/claude-code / deletePluginOptions

Function deletePluginOptions

src/utils/plugins/pluginOptionsStorage.ts:210–273  ·  view source on GitHub ↗
(pluginId: string)

Source from the content-addressed store, hash-verified

208 * "uninstall failed" message for a cleanup side-effect.
209 */
210export function deletePluginOptions(pluginId: string): void {
211 // Settings side — also wipes the legacy mcpServers sub-key (same story:
212 // orphaned on uninstall, never cleaned up before this PR).
213 //
214 // Use `undefined` (not `delete`) because `updateSettingsForSource` merges
215 // via `mergeWith` — absent keys are ignored, only `undefined` triggers
216 // removal. Cast is deliberate (CLAUDE.md's 10% case): adding z.undefined()
217 // to the schema instead (like enabledPlugins:466 does) leaks
218 // `| {[k: string]: unknown}` into the public SDK type, which subsumes the
219 // real object arm and kills excess-property checks for SDK consumers. The
220 // mergeWith-deletion contract is internal plumbing — it shouldn't shape
221 // the Zod schema. enabledPlugins gets away with it only because its other
222 // arms (string[] | boolean) are non-objects that stay distinct.
223 const settings = getSettings_DEPRECATED()
224 type PluginConfigs = NonNullable<typeof settings.pluginConfigs>
225 if (settings.pluginConfigs?.[pluginId]) {
226 // Partial<Record<K,V>> = Record<K, V | undefined> — gives us the widening
227 // for the undefined value, and Partial-of-X overlaps with X so the cast
228 // is a narrowing TS accepts (same approach as marketplaceManager.ts:1795).
229 const pluginConfigs: Partial<PluginConfigs> = { [pluginId]: undefined }
230 const { error } = updateSettingsForSource('userSettings', {
231 pluginConfigs: pluginConfigs as PluginConfigs,
232 })
233 if (error) {
234 logForDebugging(
235 `deletePluginOptions: failed to clear settings.pluginConfigs[${pluginId}]: ${error.message}`,
236 { level: 'warn' },
237 )
238 }
239 }
240
241 // Secure storage side — delete both the top-level pluginSecrets[pluginId]
242 // and any per-server composite keys `${pluginId}/${server}` (from
243 // saveMcpServerUserConfig's sensitive split). `/` prefix match is safe:
244 // plugin IDs are `name@marketplace`, never contain `/`, so
245 // startsWith(`${id}/`) can't false-positive on a different plugin.
246 const storage = getSecureStorage()
247 const existing = storage.read()
248 if (existing?.pluginSecrets) {
249 const prefix = `${pluginId}/`
250 const survivingEntries = Object.entries(existing.pluginSecrets).filter(
251 ([k]) => k !== pluginId && !k.startsWith(prefix),
252 )
253 if (
254 survivingEntries.length !== Object.keys(existing.pluginSecrets).length
255 ) {
256 const result = storage.update({
257 ...existing,
258 pluginSecrets:
259 survivingEntries.length > 0
260 ? Object.fromEntries(survivingEntries)
261 : undefined,
262 })
263 if (!result.success) {
264 logForDebugging(
265 `deletePluginOptions: failed to clear pluginSecrets for ${pluginId} from keychain`,
266 { level: 'warn' },
267 )

Callers 2

removeMarketplaceSourceFunction · 0.85
uninstallPluginOpFunction · 0.85

Calls 8

updateSettingsForSourceFunction · 0.85
logForDebuggingFunction · 0.85
getSecureStorageFunction · 0.85
clearPluginOptionsCacheFunction · 0.85
entriesMethod · 0.80
keysMethod · 0.80
readMethod · 0.65
updateMethod · 0.65

Tested by

no test coverage detected