Skip to content
源码分析手册

Plugin Marketplace:不是记一个 URL,而是一套声明、物化与调和的三层逻辑

对应官方文档:claude-code-docs/docs/discover-plugins.md 里的 How marketplaces work / Add marketplaces

核心概念

在许多工具中,“添加源”可能只是在配置文件里多写一行地址,等到需要下载时再临时访问。但 Claude Code 的 Marketplace 机制要复杂得多。

它本质上是一个本地 Catalog 物料管理层。添加 Marketplace 的过程不是“记录一个 URL”,而是:

  1. 物化 Catalog:将远端(GitHub/Git/NPM/URL)的清单克隆并“固化”到本地 ~/.claude/plugins/marketplaces/ 下。
  2. 状态登记:在 known_marketplaces.json 里登记这个 Marketplace 的本地路径和元数据。
  3. 持久化管理:通过一个“调和器 (Reconciler)”不断确保本地物料与用户的配置意图(Intent)保持同步。

这种设计的核心是让插件源变得离线可用、快速可查、且能被企业策略 (Policy) 精准控制

源码级拆解

Claude Code 管理 Marketplace 的核心是 reconciler.tsmarketplaceManager.ts,分为四个关键步骤:

1. 意图声明与默认策略 (Intent Declaration)

getDeclaredMarketplaces() 会从 settings.jsonextraKnownMarketplaces 中提取用户添加的源。 此外,它还包含一个隐藏逻辑:如果你在设置中启用了 @claude-plugins-official 作用域下的插件,系统会自动将官方的 Anthropic Marketplace 声明进来,无需手动执行 add

2. 状态差异分析 (The Diffing)

src/utils/plugins/reconciler.ts 中的 diffMarketplaces(...) 是核心逻辑。它会拿“当前的配置意图 (Intent)”去对比“磁盘上的真实物化状态 (known_marketplaces.json)”,并将差异分为三类:

  • missing:设置里声明了,但本地还没物化。
  • sourceChanged: Marketplace 名字相同,但源地址(比如从 HTTPS 改成了 SSH)已经变了。
  • upToDate:本地状态与配置一致。

3. 后台物化调和 (Background Reconcile)

PluginInstallationManager.ts 中,系统会在后台异步运行 reconcileMarketplaces()。 它不是直接覆盖磁盘,而是基于 diff 结果按需补齐。特别是对于 missingsourceChanged 的项,它会调用 addMarketplaceSource(...) 进行克隆或拉取。 这个过程是增量且幂等的。如果源地址没变,它绝不会浪费流量重新下载。

4. 自动更新与刷新策略 (Auto-Update Policy)

Marketplace 并不总是在启动时更新所有内容。pluginAutoupdate.ts 会检查 autoUpdate 标志:

  • 官方源:默认 autoUpdate: true
  • 第三方源:默认 false(除非在 settings 中显式设为 true)。 这种区分策略在保证核心体验流畅的同时,也避免了因频繁拉取不可信源而产生的启动延迟或安全隐患。

实战注意事项

  1. Add Marketplace != Install Plugin:添加 Marketplace 只是把货架摆到了店里。货架上有什么(Catalog)你可以看到了,但要把货(Plugin)装进系统,还需要执行 plugin install
  2. 意图 (Intent) 决定状态 (Materialization):如果你从 settings.json 里删掉了一个 Marketplace 声明,它在 known_marketplaces.json 里的登记依然可能存在。Claude Code 的调和器目前主要是增量式 (Additive) 的,它不主动帮你删除那些已经失去声明的“流浪”Marketplace 物料。
  3. Policy 检查发生在克隆前:如果你添加了一个被企业 Policy 禁用的源,marketplaceManager.ts 会在发起任何网络请求前直接拦截它。
  4. Marketplace 刷新不代表插件热切:Catalog 更新成功后,当前会话的插件并不会自动变强。你可能需要收到提示后手动执行 /reload-plugins

延伸阅读

  • 如果你想看 Marketplace 里的条目是如何转换成具体的插件物料的,请看 plugin-caching-and-resolution.md
  • 如果你更关心插件版本是如何通过 Marketplace 里的 Commit SHA 或版本号确定的,请看 version-resolution.md

源码锚点

  • claude-code-opensource/src/utils/plugins/marketplaceManager.ts
📄 src/utils/plugins/marketplaceManager.tsL80-93 of 2644
typescript
  type KnownMarketplace,
  type KnownMarketplacesFile,
  KnownMarketplacesFileSchema,
  type MarketplaceSource,
  type PluginMarketplace,
  type PluginMarketplaceEntry,
  PluginMarketplaceSchema,
  validateOfficialNameSource,
} from './schemas.js'

/**
 * Result of loading and caching a marketplace
 */
type LoadedPluginMarketplace = {
Marketplace 的底层读写和 Catalog 缓存逻辑。
  • claude-code-opensource/src/utils/plugins/reconciler.ts
📄 src/utils/plugins/reconciler.tsL26-30 of 266
typescript
  type KnownMarketplacesFile,
  type MarketplaceSource,
} from './schemas.js'

export type MarketplaceDiff = {
`diffMarketplaces` 和 `reconcileMarketplaces` 的实现,它是 Intent 到 Materialization 的核心流程。
  • claude-code-opensource/src/utils/plugins/pluginAutoupdate.ts
📄 src/utils/plugins/pluginAutoupdate.tsL35-64 of 285
typescript
export type PluginAutoUpdateCallback = (updatedPlugins: string[]) => void

// Store callback for plugin update notifications
let pluginUpdateCallback: PluginAutoUpdateCallback | null = null

// Store pending updates that occurred before callback was registered
// This handles the race condition where updates complete before REPL mounts
let pendingNotification: string[] | null = null

/**
 * Register a callback to be notified when plugins are auto-updated.
 * This is used by the REPL to show restart notifications.
 *
 * If plugins were already updated before the callback was registered,
 * the callback will be invoked immediately with the pending updates.
 */
export function onPluginsAutoUpdated(
  callback: PluginAutoUpdateCallback,
): () => void {
  pluginUpdateCallback = callback

  // If there are pending updates that happened before registration, deliver them now
  if (pendingNotification !== null && pendingNotification.length > 0) {
    callback(pendingNotification)
    pendingNotification = null
  }

  return () => {
    pluginUpdateCallback = null
  }
实现了 Marketplace 的自动刷新策略(官方 vs 第三方)。
  • claude-code-opensource/src/utils/plugins/officialMarketplace.ts
📄 src/utils/plugins/officialMarketplace.tsL15-18 of 26
typescript
export const OFFICIAL_MARKETPLACE_SOURCE = {
  source: 'github',
  repo: 'anthropics/claude-plugins-official',
} as const satisfies MarketplaceSource
定义了官方 Marketplace 的预设源地址和保留名称规则。
  • claude-code-opensource/src/services/plugins/PluginInstallationManager.ts
📄 src/services/plugins/PluginInstallationManager.tsL25-48 of 185
typescript
type SetAppState = (f: (prevState: AppState) => AppState) => void

/**
 * Update marketplace installation status in app state
 */
function updateMarketplaceStatus(
  setAppState: SetAppState,
  name: string,
  status: 'pending' | 'installing' | 'installed' | 'failed',
  error?: string,
): void {
  setAppState(prevState => ({
    ...prevState,
    plugins: {
      ...prevState.plugins,
      installationStatus: {
        ...prevState.plugins.installationStatus,
        marketplaces: prevState.plugins.installationStatus.marketplaces.map(
          m => (m.name === name ? { ...m, status, error } : m),
        ),
      },
    },
  }))
}
将 Marketplace 的调和过程挂载到启动流程中的后台任务。

基于 Claude Code v2.1.88 开源快照的深度分析