Bundling — Plugin Bundle Discovery & Packaging
Manifest parsing, command discovery, and MCP config for Codex/Claude/Cursor bundles
The bundling sub-module handles plugin bundles — pre-packaged plugin directories that follow Codex, Claude, or Cursor conventions. It reads bundle manifest files (.codex-plugin/plugin.json, .claude-plugin/plugin.json, .cursor-plugin/plugin.json), extracts skills, hooks, commands, and MCP server configurations. This enables OpenClaw to discover and activate plugins that were originally built for other AI coding assistants.
Multi-Format Manifest-First Command Discovery MCP Config
📚 What is this & why does it exist?
The bundling sub-module handles plugin bundles — pre-packaged plugin directories that follow Codex, Claude, or Cursor conventions. It reads bundle manifest files (.codex-plugin/plugin.json, .claude-plugin/plugin.json, .cursor-plugin/plugin.json), extracts skills, hooks, commands, and MCP server configurations. This enables OpenClaw to discover and activate plugins that were originally built for other AI coding assistants.
💡 Real-world analogy
Imagine a universal power adapter for international travel. Different countries (Codex, Claude, Cursor) have different plug shapes (manifest formats). The bundling module is the adapter — it reads each format’s manifest, normalizes the metadata into OpenClaw’s standard BundlePluginManifest, and exposes the capabilities (skills, hooks, commands, MCP servers) through a common interface.
Contents
Architecture
flowchart TD
subgraph Formats["Bundle Formats"]
Codex[".codex-plugin/plugin.json"]
Claude[".claude-plugin/plugin.json"]
Cursor[".cursor-plugin/plugin.json"]
end
subgraph Manifest["Manifest Processing"]
Detect["detectBundleManifestFormat()"]
Load["loadBundleManifest()"]
Normalize["Normalize to BundlePluginManifest"]
end
subgraph Commands["Command Discovery"]
CmdRoots["resolveClaudeCommandRootDirs()"]
MDScan["listMarkdownFilesRecursive()"]
Parse["parseFrontmatterBlock()"]
CmdSpec["ClaudeBundleCommandSpec[]"]
end
subgraph MCP["MCP Configuration"]
MCPPaths["resolveBundleMcpConfigPaths()"]
Extract["extractMcpServerMap()"]
Merge["applyMergePatch()"]
MCPConfig["BundleMcpConfig"]
end
Codex --> Detect
Claude --> Detect
Cursor --> Detect
Detect --> Load
Load --> Normalize
Normalize --> CmdRoots
CmdRoots --> MDScan
MDScan --> Parse
Parse --> CmdSpec
Normalize --> MCPPaths
MCPPaths --> Extract
Extract --> Merge
Merge --> MCPConfig
Key Types
| Name | File | Type | Description |
|---|---|---|---|
BundlePluginManifest | bundle-manifest.ts | Type | Normalized bundle metadata: id, skills, hooks, capabilities, bundleFormat |
BundleManifestLoadResult | bundle-manifest.ts | Union | Result type for manifest loading (ok with manifest or error) |
ClaudeBundleCommandSpec | bundle-commands.ts | Type | Parsed command: pluginId, rawName, description, promptTemplate, sourceFilePath |
BundleMcpServerConfig | bundle-mcp.ts | Type | MCP server configuration (arbitrary key-value) |
BundleMcpConfig | bundle-mcp.ts | Type | Collection of MCP server configurations keyed by server name |
BundleMcpRuntimeSupport | bundle-mcp.ts | Type | Runtime MCP support: stdio servers, supported/unsupported names, diagnostics |
PluginBundleFormat | types.ts | Union | Bundle format discriminator: 'claude' | 'codex' | 'cursor' |
Data Flow
sequenceDiagram
participant Disc as Plugin Discovery
participant BM as bundle-manifest.ts
participant BC as bundle-commands.ts
participant BMCP as bundle-mcp.ts
participant Reg as Plugin Registry
Disc->>BM: detectBundleManifestFormat(rootDir)
BM-->>Disc: format ('claude'|'codex'|'cursor')
Disc->>BM: loadBundleManifest(rootDir, format)
BM->>BM: Read format-specific manifest file
BM->>BM: normalizeBundlePathList(skills, hooks, capabilities)
BM-->>Disc: BundleManifestLoadResult
Disc->>BC: discoverBundleCommands(rootDir, config)
BC->>BC: resolveClaudeCommandRootDirs()
BC->>BC: listMarkdownFilesRecursive()
BC->>BC: parseFrontmatterBlock() per .md
BC-->>Disc: ClaudeBundleCommandSpec[]
Disc->>BMCP: loadEnabledBundleMcpConfig(rootDir)
BMCP->>BMCP: resolveBundleMcpConfigPaths()
BMCP->>BMCP: extractMcpServerMap()
BMCP-->>Disc: EnabledBundleMcpConfigResult
Disc->>Reg: Register capabilities
Deep-Dive Highlights
Core bundling mechanisms have been extracted into dedicated focus pages for in-depth exploration. Each page covers one aspect of the bundling pipeline with diagrams, code walks, and configuration details.
Deep-Dive Pages
📦 Bundle Manifest Focus
Multi-format manifest detection, loading, and normalization for Codex/Claude/Cursor plugins.
⚡ Bundle Commands Focus
Markdown-based command discovery with frontmatter parsing and prompt templates.
🎯 Bundled Capabilities Focus
Runtime capability registration for bundled plugins with Vitest compatibility.
Directory Map
Source tree for bundling
📁 src/plugins/ (bundling-related files)
├── bundle-manifest.ts # Multi-format manifest loading & normalization
├── bundle-commands.ts # Markdown command discovery & frontmatter parsing
├── bundle-mcp.ts # MCP server config extraction & merging
├── bundle-lsp.ts # LSP integration for bundled plugins
├── bundled-sources.ts # Bundled plugin source resolution
├── bundled-dir.ts # Bundled directory management
├── bundled-compat.ts # Compatibility layer for bundled plugins
├── bundled-capability-runtime.ts # Runtime config for bundled capabilities
├── bundled-plugin-metadata.ts # Plugin metadata extraction
├── bundled-capability-metadata.ts # Capability metadata
└── bundled-web-search*.ts # Bundled web search providers
Code Examples
Example: Loading a Bundle Manifest
// Loading a bundle manifest
import { detectBundleManifestFormat, loadBundleManifest } from "./bundle-manifest.js";
const format = detectBundleManifestFormat(pluginRootDir);
if (format) {
const result = loadBundleManifest(pluginRootDir, format);
if (result.ok) {
console.log("Plugin:", result.manifest.id);
console.log("Skills:", result.manifest.skills);
console.log("MCP Servers:", result.manifest.capabilities);
}
}