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

Function loadPluginsFromMarketplaces

src/utils/plugins/pluginLoader.ts:1888–2089  ·  view source on GitHub ↗

* Shared discovery/policy/merge pipeline for both load modes. * * Resolves enabledPlugins → marketplace entries, runs enterprise policy * checks, pre-loads catalogs, then dispatches each entry to the full or * cache-only per-entry loader. The ONLY difference between loadAllPlugins * and loadAll

({
  cacheOnly,
}: {
  cacheOnly: boolean
})

Source from the content-addressed store, hash-verified

1886 * are identical.
1887 */
1888async function loadPluginsFromMarketplaces({
1889 cacheOnly,
1890}: {
1891 cacheOnly: boolean
1892}): Promise<{
1893 plugins: LoadedPlugin[]
1894 errors: PluginError[]
1895}> {
1896 const settings = getSettings_DEPRECATED()
1897 // Merge --add-dir plugins at lowest priority; standard settings win on conflict
1898 const enabledPlugins = {
1899 ...getAddDirEnabledPlugins(),
1900 ...(settings.enabledPlugins || {}),
1901 }
1902 const plugins: LoadedPlugin[] = []
1903 const errors: PluginError[] = []
1904
1905 // Filter to plugin@marketplace format and validate
1906 const marketplacePluginEntries = Object.entries(enabledPlugins).filter(
1907 ([key, value]) => {
1908 // Check if it's in plugin@marketplace format (includes both enabled and disabled)
1909 const isValidFormat = PluginIdSchema().safeParse(key).success
1910 if (!isValidFormat || value === undefined) return false
1911 // Skip built-in plugins — handled separately by getBuiltinPlugins()
1912 const { marketplace } = parsePluginIdentifier(key)
1913 return marketplace !== BUILTIN_MARKETPLACE_NAME
1914 },
1915 )
1916
1917 // Load known marketplaces config to look up sources for policy checking.
1918 // Use the Safe variant so a corrupted config file doesn't crash all plugin
1919 // loading — this is a read-only path, so returning {} degrades gracefully.
1920 const knownMarketplaces = await loadKnownMarketplacesConfigSafe()
1921
1922 // Fail-closed guard for enterprise policy: if a policy IS configured and we
1923 // cannot resolve a marketplace's source (config returned {} due to corruption,
1924 // or entry missing), we must NOT silently skip the policy check and load the
1925 // plugin anyway. Before Safe, a corrupted config crashed everything (loud,
1926 // fail-closed). With Safe + no guard, the policy check short-circuits on
1927 // undefined marketplaceConfig and the fallback path (getPluginByIdCacheOnly)
1928 // loads the plugin unchecked — a silent fail-open. This guard restores
1929 // fail-closed: unknown source + active policy → block.
1930 //
1931 // Allowlist: any value (including []) is active — empty allowlist = deny all.
1932 // Blocklist: empty [] is a semantic no-op — only non-empty counts as active.
1933 const strictAllowlist = getStrictKnownMarketplaces()
1934 const blocklist = getBlockedMarketplaces()
1935 const hasEnterprisePolicy =
1936 strictAllowlist !== null || (blocklist !== null && blocklist.length > 0)
1937
1938 // Pre-load marketplace catalogs once per marketplace rather than re-reading
1939 // known_marketplaces.json + marketplace.json for every plugin. This is the
1940 // hot path — with N plugins across M marketplaces, the old per-plugin
1941 // getPluginByIdCacheOnly() did 2N config reads + N catalog reads; this does M.
1942 const uniqueMarketplaces = new Set(
1943 marketplacePluginEntries
1944 .map(([pluginId]) => parsePluginIdentifier(pluginId).marketplace)
1945 .filter((m): m is string => !!m),

Callers 1

pluginLoader.tsFile · 0.85

Calls 15

getAddDirEnabledPluginsFunction · 0.85
parsePluginIdentifierFunction · 0.85
getBlockedMarketplacesFunction · 0.85
getMarketplaceCacheOnlyFunction · 0.85
formatSourceForDisplayFunction · 0.85
isSourceAllowedByPolicyFunction · 0.85
isSourceInBlocklistFunction · 0.85
getPluginByIdCacheOnlyFunction · 0.85

Tested by

no test coverage detected