Skip to content

MCP, plugins, and hooks

This page reverse-engineers the MCP, plugin, and hook surfaces in the analyzed cli.renamed.js.

Use Hooks and events reference for the canonical hook/frame/method list and Tool inventory and schemas for MCP/plugin tool schema ownership. This page owns MCP/plugin runtime wiring.

Source anchors

Semantic aliasString or symbolMeaning
McpCommandRegistrarfunction rR4(H)Registers the mcp command family.
McpServeCommandcommand("serve")Starts the Claude Code MCP server.
McpListCommandcommand("list")Lists configured MCP servers.
McpDesktopImportCommandcommand("add-from-claude-desktop")Imports MCP servers from Claude Desktop.
McpProjectChoiceResetreset-project-choicesResets approved/rejected project-scoped MCP choices.
McpNonblockingGateMCP_CONNECTION_NONBLOCKINGRuntime MCP connection non-blocking gate.
McpToolTimeoutMCP_TIMEOUTMCP tool timeout environment variable.
McpConfigFlag--mcp-config <configs...>Loads MCP config from JSON files or strings.
StrictMcpConfigFlag--strict-mcp-configIgnores non-flag MCP configurations.
PluginCommandRegistrarfunction fC4(H)Registers plugin / plugins command family.
PluginTuiManagerdescription:"Manage Claude Code plugins"In-session slash/TUI plugin management surface.
PluginMarketplaceCommandManage Claude Code marketplacesCLI marketplace subcommand root.
PluginMarketplaceAddCommandAdd a marketplace from a URL, path, or GitHub repoMarketplace add command and source ingestion surface.
EnabledPluginsSettingenabledPluginsSettings key storing enabled plugin IDs and version constraints.
ExtraMarketplacesSettingextraKnownMarketplacesSettings key registering repository/team marketplaces.
StrictMarketplacePolicystrictKnownMarketplacesManaged allowlist for marketplace sources.
BlockedMarketplacePolicyblockedMarketplacesManaged blocklist for marketplace sources.
DependencyMarketplacePolicyBlockdependency-marketplace-blocked-by-policyPlugin dependency install can be blocked by marketplace policy.
PluginInstallTelemetryplugin_installedPlugin install telemetry after dependency resolution and settings write.
PluginUninstallTelemetrytengu_plugin_uninstalled_cliCLI uninstall telemetry and orphan-scan follow-up.
PluginDisableTelemetrytengu_plugin_disabled_cliCLI disable telemetry.
PluginUpdateTelemetrytengu_plugin_updated_cliCLI update telemetry after version check.
PluginCacheStaginggenerateTemporaryCacheNameForPlugin, cachePluginPlugin sources are staged in a temporary cache directory before manifest validation.
PluginManifestLoadloadPluginManifestCache materialization depends on .claude-plugin/plugin.json or alternate manifest paths.
MarketplaceTargetedBulkUpdatemarketplaceUpdateHandler, tengu_marketplace_updated_allMarketplace update has targeted and all-marketplace branches.
HookEventSchemaPreToolUse, PostToolUse, SessionStart, SessionEndHook event schema.

Bundle modules in cli.renamed.js

Semantic aliasLoader line(s)Representative renamed exportsAtlas entry
McpChromeBridge13823, 13838, 39107, 39134createBridgeClient, createClaudeForChromeMcpServer, createChromeSocketClient, BridgeClient, BROWSER_TOOLS, ToolCallTimeoutError, SocketConnectionError, NoExtensionConnectedError, ExtensionDisconnectedMidCallError, DISCOVERY_TIMEOUT_MS, PEER_WAIT_TIMEOUT_MS, WAIT_MAX_DURATION_SBundle module map — integrations (MCP, plugins, MCPB, LSP)
McpbExtensionPackaging268617, 271802, 271894, 272295, 272401verifyMcpbFile, signMcpbFile, unsignMcpbFile, unpackExtension, verifyCertificateChain, validateManifest, readMcpbIgnorePatterns, readPackageJson, replaceVariables, shouldExclude, promptVisualAssets, promptUserConfigBundle module map — integrations (MCP, plugins, MCPB, LSP)
PluginLoader276711loadAllPlugins, loadAllPluginsCacheOnly, loadPluginManifest, loadSkillsAsPlugins, resolvePluginPath, resolveContainedPluginPath, mergePluginSources, probeSeedCacheAnyVersionBundle module map — integrations (MCP, plugins, MCPB, LSP)
PluginCommandHandlers644148pluginInstallHandler, pluginUninstallHandler, pluginListHandler, pluginInitHandler, pluginTagHandler, pluginPruneHandler, pluginValidateHandler, pluginUpdateHandlerBundle module map — integrations (MCP, plugins, MCPB, LSP)
HookEventDispatcher629735shouldSkipHookDueToTrust, hasWorktreeCreateHook, hasInstructionsLoadedHook, getUserPromptSubmitHookBlockingMessage, persistHookOutput, parseElicitationHookOutput, getTaskCreatedHookMessage, getTaskCompletedHookMessageBundle module map — permission, trust, hooks, and policy

MCP runtime map

flowchart TD
Flags[--mcp-config / --strict-mcp-config] --> Config[MCP config set]
Settings[settings / project / managed policy] --> Config
Plugins[plugin-provided MCP] --> Config
Config --> Coordinator[MCP runtime coordinator]
Coordinator --> Always[alwaysLoad servers]
Coordinator --> Deferred[non-alwaysLoad servers]
Coordinator --> ClaudeAi[claude.ai connectors]
Coordinator --> Tools[MCP tools/resources/prompts]

McpRuntimeCoordinator splits regular configs into always-load and non-always-load groups, honors MCP_CONNECTION_NONBLOCKING, connects regular and claude.ai connector configs, and deduplicates overlapping plugin/connector surfaces.

MCP command surface

SubcommandRuntime role
serveStarts Claude Code’s MCP server.
listLists configured MCP servers; help text warns that stdio servers can be spawned for health checks.
add, remove, get, add-jsonPresent in the command family by surrounding command registration, though individual anchors are less stable than McpCommandRegistrar.
add-from-claude-desktopImports MCP servers from Claude Desktop on supported platforms.
reset-project-choicesClears project-scoped approve/reject choices for .mcp.json servers.

Plugin surfaces

PluginCommandRegistrar registers plugin / plugins. High-signal plugin strings show:

  • session-only plugins through --plugin-dir and --plugin-url;
  • marketplace concepts and reserved marketplace names;
  • plugin-provided agents, skills, hooks, mcpServers, and outputStyles schema surfaces;
  • plugin autoupdate guarded by updater state.

Plugin marketplace lifecycle

The plugin path has two user-facing entry surfaces: an in-session slash/TUI plugin menu and the CLI plugin / plugins command family. The CLI tree contains a marketplace root with add/list/update/remove-style wording, while settings and policy carry the durable state.

flowchart TD
Source[Marketplace source: URL / path / GitHub repo / settings] --> Register[extraKnownMarketplaces]
Policy[strictKnownMarketplaces / blockedMarketplaces] --> Register
Register --> Catalog[Known marketplace catalog]
Catalog --> Install[Install plugin + dependencies]
Install --> Enabled[enabledPlugins setting]
Enabled --> Runtime[hooks / MCP / skills / agents / output styles]
Runtime --> Reload[plugin-affecting settings reload]
StageSource-visible evidenceRuntime implication
Register marketplacemarketplace add <source>, extraKnownMarketplaces, sparse checkout and scope optionsMarketplaces can be declared from CLI or settings and scoped to user/project/local contexts.
Apply policystrictKnownMarketplaces, blockedMarketplaces, dependency-marketplace-blocked-by-policyManaged policy can allowlist or block sources before plugin/dependency installation completes.
Install pluginplugin_installed, dependency closure handling, enabledPlugins writesInstall resolves dependency closure, writes enabled plugin state, and emits plugin/marketplace telemetry.
Update plugintengu_plugin_updated_cli, autoUpdate settingsUpdates can be triggered from CLI and may be gated by marketplace auto-update configuration.
Disable or uninstalltengu_plugin_disabled_cli, tengu_plugin_uninstalled_cliDisable changes enabled state; uninstall can also run orphan scans/pruning.
Runtime contributionplugin hooks, mcpServers, skills, agents, outputStylesEnabled plugins become capability injectors, but still flow through settings, hooks, MCP, and permission boundaries.

This narrows the previous marketplace gap from “command surfaces only” to a lifecycle model: source registration, policy filtering, dependency resolution, enabled-state writes, update/disable/uninstall operations, and runtime capability reloads. The remaining gap is exact per-command UI flow and every option branch inside the lazy-loaded marketplace handlers.

Plugin cache staging

The decoded plugin-cache chunk shows a write-then-validate flow. cachePlugin creates a temporary cache directory under the plugin cache root, copies or installs the source there, then calls loadPluginManifest before returning a materialized plugin record. The observed temporary name includes source kind, timestamp, and a random suffix (temp_<source>_<time>_<rand>), but the stable behavior to rely on is the staging boundary: source acquisition happens before manifest validation and final cache reuse.

Supported source branches include local copies, npm packages, GitHub/git URLs, and git subdirectories. The same chunk also preserves safety details such as contained path resolution, safe symlink handling during local copy, and POSIX filtering for exported plugin bin paths.

marketplaceUpdateHandler has two visible branches: a named marketplace update emits tengu_marketplace_updated, while the no-name branch loads all configured marketplaces and emits tengu_marketplace_updated_all with a count. Individual plugin update still delegates to a lower-level updater, so this page documents the update surface and telemetry rather than every update/install branch.

Hook events

The hook schema includes these high-signal lifecycle events:

Hook familyExamples
Tool lifecyclePreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch
Prompt/session lifecycleUserPromptSubmit, UserPromptExpansion, SessionStart, SessionEnd
Stop/compactionStop, StopFailure, PreCompact, PostCompact
Agent/task lifecycleSubagentStart, SubagentStop, TaskCreated, TaskCompleted
Permission/config/worktreePermissionRequest, PermissionDenied, ConfigChange, WorktreeCreate, WorktreeRemove

Security interpretation

MCP and plugins extend model-visible capabilities, but they do not bypass the rest of the runtime. The same command surface also exposes strict config, project-choice reset, hook validation, timeouts, and permission-prompt routing, which indicates that external capabilities are integrated through guarded runtime boundaries.

MCP runtime internals

This section separates MCP command management from runtime MCP connection and event handling.

Additional anchors

Semantic aliasString or symbolMeaning
McpRuntimeCoordinatorfunction fH9(H)Runtime MCP connection coordinator.
RequiredMcpConfigSplitalwaysLoad===!0Split between required and normal MCP configs.
RequiredMcpConnectLabel--mcp-config alwaysLoad serversRequired MCP config connect label.
ClaudeAiConnectorLabelclaude.ai connectorsRuntime connector connect label.
McpToolsListSchematools/listMCP tools/list protocol schema.
McpResourceListSchemasresources/list, resources/templates/listMCP resource listing schemas.
McpPromptSchemasprompts/list, prompts/getMCP prompt listing/get schemas.
McpToolTimeoutEnvMCP_TIMEOUTMCP tool timeout env var, defaulting to 30000 ms.
McpConnectTimeoutEnvMCP_CONNECT_TIMEOUT_MSMCP connection timeout env var, defaulting to 5000 ms.
McpAuthErrorTelemetrytengu_mcp_tool_call_auth_errorMCP tool-call auth-expiry telemetry/error path.
McpElicitationFrameelicitation_completeMCP elicitation completion is bridged into headless frames.

Runtime coordinator

The complete McpRuntimeCoordinator function is small enough to reconstruct:

  1. Receives regularMcpConfigs, claudeaiConfigPromise, and state.
  2. Computes a non-blocking flag from MCP_CONNECTION_NONBLOCKING or an explicit nonBlocking option.
  3. Stores the non-blocking state through IV8(_).
  4. Partitions regular MCP configs into alwaysLoad === true and all other configs.
  5. Returned connect() starts two parallel connection groups: regular configs via Br6(...) and claude.ai connectors via kKA(...) after claudeaiConfigPromise resolves.
  6. Records startup markers before_mcp_connect_user, before_mcp_connect_connector, after_mcp_connect_user, after_mcp_connect_connector.
flowchart TD
Input[regularMcpConfigs + claudeaiConfigPromise] --> NonBlocking[MCP_CONNECTION_NONBLOCKING]
Input --> Split[split alwaysLoad vs normal]
Split --> Required[alwaysLoad servers]
Split --> Normal[regular servers]
Input --> Connectors[claude.ai connectors]
Required --> Connect[connect() Promise.all]
Normal --> Connect
Connectors --> Connect

Required versus non-required servers

The alwaysLoad split is important. Required servers are labeled --mcp-config alwaysLoad servers and normal servers as --mcp-config servers. Required servers are connected as a distinct group; normal servers can honor non-blocking/deferred behavior; claude.ai connectors are a separate promise-driven group. Because connect() awaits both groups, the final headless startup waits for the coordinator as a whole even when parts are internally non-blocking.

MCP protocol surfaces

Protocol methodRole
tools/listLists tools exposed by an MCP server.
resources/listLists resources.
resources/templates/listLists resource templates.
prompts/listLists prompts.
prompts/getRetrieves a prompt by name/arguments.

These schema anchors are paired with runtime anchors in McpRuntimeCoordinator, McpCommandRegistrar, HeadlessControlLoop, and MCP tool-call error handling, confirming MCP as both a command/config system and a runtime capability provider.

Timeout and auth-error mechanics

  • MCP_TIMEOUT → tool-call timeout, falling back to 30000 ms.
  • MCP_CONNECT_TIMEOUT_MS → connection timeout, falling back to 5000 ms.

The MCP tool-call error path has a specific auth branch: on 401 or token-expiry-like errors, the runtime emits tengu_mcp_tool_call_auth_error and raises a user-facing error requiring re-authorization.

Elicitation and headless integration

MCP elicitation completion is visible in two places: the MCP client handles an elicitation completion notification and marks queued elicitation state as complete; HeadlessControlLoop emits system / elicitation_complete frames with mcp_server_name and elicitation_id. URL-mode elicitation is observable as a headless/SDK stream frame.

Command tree versus runtime connection

SurfaceFunctionRole
claude mcp ...McpCommandRegistrarManage config and run the MCP server.
root action headless setupMcpRuntimeCoordinatorConnect runtime MCP servers/connectors for a session.
MCP protocol schemasline ~95 schemasDefine method/result shapes.
Headless stream loopHeadlessControlLoopEmits MCP-related status, elicitation, auth, and tool-call state to headless consumers.

Plugin hot reload and hook cache

The PluginHotReload module (cli.renamed.js:288518-288560) keeps plugin-contributed hooks fresh without restarting the session. The PluginInstallLifecycle module (cli.renamed.js:545539-545700, plus kickOffBackgroundPluginInstall at 722995) handles plugin discovery, installation, and pruning.

Plugin-affecting settings snapshot

getPluginAffectingSettingsSnapshot() builds a JSON-stable snapshot of the slice of settings that influence which plugins are active (enabledPlugins, marketplace lists, etc.). The watcher compares the new snapshot against the previous one; when they differ, plugin hooks are re-loaded.

Hot-reload runtime (setupPluginHookHotReload, loadPluginHooks)

setupPluginHookHotReload():

  1. Subscribes to settings changes for the slice returned by getPluginAffectingSettingsSnapshot().
  2. On change, calls pruneRemovedPluginHooks() (removes hooks whose owning plugin is no longer enabled) and loadPluginHooks() (re-registers hooks for currently enabled plugins).
  3. Caches the materialized hook records so the hook dispatcher does not re-parse plugin manifests on every dispatch.

clearPluginHookCache() is the brute-force invalidator used after major settings churn (settings file replaced wholesale, marketplace re-installed). resetHotReloadState() clears the watcher state so tests can rebuild it cleanly.

pruneRemovedPluginHooks() walks the hook cache, finds entries whose pluginId is no longer in enabledPlugins, and removes them from the dispatcher cache. This is also the path that kicks in when a user disables a plugin via /plugin disable.

Install lifecycle (checkEnabledPlugins, getInstalledPlugins, findMissingPlugins, installSelectedPlugins)

  • checkEnabledPlugins() compares the enabledPlugins setting (per source) against the on-disk plugin cache. Returns a structured result listing missing installs and version mismatches.
  • getInstalledPlugins() enumerates plugins materialized in the cache (~/.claude/plugins/cache/...).
  • findMissingPlugins(enabled) returns the subset of enabledPlugins whose cache entries are missing or out of date.
  • installSelectedPlugins(selection, options, source = "user") materializes the selection: stages downloads in a temp cache (via generateTemporaryCacheNameForPlugin / cachePlugin), validates the manifest (loadPluginManifest), then promotes to the live cache. The source argument controls which settings tier records the install.
  • getPluginEditableScopes() / isPersistableScope(scope) / settingSourceToScope(source) decide where install records may be written (e.g. managed-policy installs are not editable from the user shell).

Background install (kickOffBackgroundPluginInstall)

kickOffBackgroundPluginInstall(missing) schedules installSelectedPlugins(missing, {...}) to run in the background after the current turn settles. It is the path used during session startup when enabledPlugins references a plugin that is not yet installed: the runtime announces the install in the UI but does not block the session on the download. Failures surface as task notifications.

Created and maintained by Yingting Huang.