MCPcopy Create free account
hub / github.com/claude-code-best/claude-code / saveMcpServerUserConfig

Function saveMcpServerUserConfig

src/utils/plugins/mcpbHandler.ts:203–351  ·  view source on GitHub ↗
(
  pluginId: string,
  serverName: string,
  config: UserConfigValues,
  schema: UserConfigSchema,
)

Source from the content-addressed store, hash-verified

201 * or channels[].userConfig) — drives the sensitive/non-sensitive split
202 */
203export function saveMcpServerUserConfig(
204 pluginId: string,
205 serverName: string,
206 config: UserConfigValues,
207 schema: UserConfigSchema,
208): void {
209 try {
210 const nonSensitive: UserConfigValues = {}
211 const sensitive: Record<string, string> = {}
212
213 for (const [key, value] of Object.entries(config)) {
214 if (schema[key]?.sensitive === true) {
215 sensitive[key] = String(value)
216 } else {
217 nonSensitive[key] = value
218 }
219 }
220
221 // Scrub ONLY keys we're writing in this call. Covers both directions
222 // across schema-version flips:
223 // - sensitive→secureStorage ⇒ remove stale plaintext from settings.json
224 // - nonSensitive→settings.json ⇒ remove stale entry from secureStorage
225 // (otherwise loadMcpServerUserConfig's {...nonSensitive, ...sensitive}
226 // would let the stale secureStorage value win on next read)
227 // Partial `config` (user only re-enters one field) leaves other fields
228 // untouched in BOTH stores — defense-in-depth against future callers.
229 const sensitiveKeysInThisSave = new Set(Object.keys(sensitive))
230 const nonSensitiveKeysInThisSave = new Set(Object.keys(nonSensitive))
231
232 // Sensitive → secureStorage FIRST. If this fails (keychain locked,
233 // .credentials.json perms), throw before touching settings.json — the
234 // old plaintext stays as a fallback instead of losing BOTH copies.
235 //
236 // Also scrub non-sensitive keys from secureStorage — schema flipped
237 // sensitive→false and they're being written to settings.json now. Without
238 // this, loadMcpServerUserConfig's merge would let the stale secureStorage
239 // value win on next read.
240 const storage = getSecureStorage()
241 const k = serverSecretsKey(pluginId, serverName)
242 const existingInSecureStorage =
243 storage.read()?.pluginSecrets?.[k] ?? undefined
244 const secureScrubbed = existingInSecureStorage
245 ? Object.fromEntries(
246 Object.entries(existingInSecureStorage).filter(
247 ([key]) => !nonSensitiveKeysInThisSave.has(key),
248 ),
249 )
250 : undefined
251 const needSecureScrub =
252 secureScrubbed &&
253 existingInSecureStorage &&
254 Object.keys(secureScrubbed).length !==
255 Object.keys(existingInSecureStorage).length
256 if (Object.keys(sensitive).length > 0 || needSecureScrub) {
257 const existing = storage.read() ?? {}
258 if (!existing.pluginSecrets) {
259 existing.pluginSecrets = {}
260 }

Callers 2

loadMcpbFileFunction · 0.85
PluginOptionsFlowFunction · 0.85

Calls 10

getSecureStorageFunction · 0.85
serverSecretsKeyFunction · 0.85
updateSettingsForSourceFunction · 0.85
toErrorFunction · 0.85
entriesMethod · 0.80
keysMethod · 0.65
logForDebuggingFunction · 0.50
logErrorFunction · 0.50
readMethod · 0.45
hasMethod · 0.45

Tested by

no test coverage detected