Skip to main content
Plugin module for OpenHands SDK. This module provides support for loading and managing plugins that bundle skills, hooks, MCP configurations, agents, and commands together. It also provides support for plugin marketplaces - directories that list available plugins with their metadata and source locations. Additionally, it provides utilities for managing installed plugins in the user’s home directory (~/.openhands/plugins/installed/).

class CommandDefinition

Bases: BaseModel Command definition loaded from markdown file. Commands are slash commands that users can invoke directly. They define instructions for the agent to follow.

Properties

  • allowed_tools: list[str]
  • argument_hint: str | None
  • content: str
  • description: str
  • metadata: dict[str, Any]
  • name: str
  • source: str | None

Methods

classmethod load()

Load a command definition from a markdown file. Command markdown files have YAML frontmatter with:
  • description: Command description
  • argument-hint: Hint for command arguments (string or list)
  • allowed-tools: List of allowed tools
The body of the markdown is the command instructions.
  • Parameters: command_path – Path to the command markdown file.
  • Returns: Loaded CommandDefinition instance.

model_config = (configuration object)

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

to_skill()

Convert this command to a keyword-triggered Skill. Creates a Skill with a KeywordTrigger using the Claude Code namespacing format: /<plugin-name>:<command-name>
  • Parameters: plugin_name – The name of the plugin this command belongs to.
  • Returns: A Skill object with the command content and a KeywordTrigger.

class GitHubURLComponents

Bases: NamedTuple Parsed components of a GitHub blob/tree URL.

Properties

  • branch: str Alias for field number 2
  • owner: str Alias for field number 0
  • path: str Alias for field number 3
  • repo: str Alias for field number 1

class InstalledPluginInfo

Bases: BaseModel Information about an installed plugin. This model tracks metadata about a plugin installation, including where it was installed from and when.

Properties

  • description: str
  • enabled: bool
  • install_path: str
  • installed_at: str
  • name: str
  • repo_path: str | None
  • resolved_ref: str | None
  • source: str
  • version: str

Methods

classmethod from_plugin()

Create InstalledPluginInfo from a loaded Plugin.

model_config = (configuration object)

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class InstalledPluginsMetadata

Bases: BaseModel Metadata file for tracking all installed plugins.

Properties

Methods

classmethod get_path()

Get the metadata file path for the given installed plugins directory.

classmethod load_from_dir()

Load metadata from the installed plugins directory.

model_config = (configuration object)

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

save_to_dir()

Save metadata to the installed plugins directory.

class Marketplace

Bases: BaseModel A plugin marketplace that lists available plugins and skills. Follows the Claude Code marketplace structure for compatibility, with an additional skills field for standalone skill references. The marketplace.json file is located in .plugin/ or .claude-plugin/ directory at the root of the marketplace repository.

Properties

Methods

get_plugin()

Get a plugin entry by name.
  • Parameters: name – Plugin name to look up.
  • Returns: MarketplacePluginEntry if found, None otherwise.

classmethod load()

Load a marketplace from a directory. Looks for marketplace.json in .plugin/ or .claude-plugin/ directories.
  • Parameters: marketplace_path – Path to the marketplace directory.
  • Returns: Loaded Marketplace instance.
  • Raises:
    • FileNotFoundError – If the marketplace directory or manifest doesn’t exist.
    • ValueError – If the marketplace manifest is invalid.

resolve_plugin_source()

Resolve a plugin’s source to a full path or URL.
  • Returns:
    • source: Resolved source string (path or URL)
    • ref: Branch, tag, or commit reference (None for local paths)
    • subpath: Subdirectory path within the repo (None if not specified)
  • Return type: Tuple of (source, ref, subpath) where

class MarketplaceEntry

Bases: BaseModel Base class for marketplace entries (plugins and skills). Both plugins and skills are pointers to directories:
  • Plugin directories contain: plugin.json, skills/, commands/, agents/, etc.
  • Skill directories contain: SKILL.md and optionally scripts/, references/, assets/
Source is a string path (local path or GitHub URL).

Properties

  • author: PluginAuthor | None
  • category: str | None
  • description: str | None
  • homepage: str | None
  • model_config: = (configuration object) Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
  • name: str
  • source: str
  • version: str | None

class MarketplaceMetadata

Bases: BaseModel Optional metadata for a marketplace.

Properties

  • description: str | None
  • model_config: = (configuration object) Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
  • version: str | None

class MarketplaceOwner

Bases: BaseModel Owner information for a marketplace. The owner represents the maintainer or team responsible for the marketplace.

Properties

  • email: str | None
  • name: str

Methods

model_config = (configuration object)

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class MarketplacePluginEntry

Bases: MarketplaceEntry Plugin entry in a marketplace. Extends MarketplaceEntry with Claude Code compatibility fields for inline plugin definitions (when strict=False). Plugins support both string sources and complex source objects (MarketplacePluginSource) for GitHub/git URLs with ref and path.

Properties

  • agents: str | list[str] | None
  • commands: str | list[str] | None
  • hooks: str | HooksConfigDict | None
  • keywords: list[str]
  • license: str | None
  • lsp_servers: LspServersDict | None
  • mcp_servers: McpServersDict | None
  • model_config: = (configuration object) Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
  • repository: str | None
  • source: str | MarketplacePluginSource
  • strict: bool
  • tags: list[str]

Methods

to_plugin_manifest()

Convert to PluginManifest (for strict=False entries).

class MarketplacePluginSource

Bases: BaseModel Plugin source specification for non-local sources. Supports GitHub repositories and generic git URLs.

Properties

  • model_config: = (configuration object) Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
  • path: str | None
  • ref: str | None
  • repo: str | None
  • source: str
  • url: str | None

Methods

validate_source_fields()

Validate that required fields are present based on source type.

class Plugin

Bases: BaseModel A plugin that bundles skills, hooks, MCP config, agents, and commands. Plugins follow the Claude Code plugin structure for compatibility: plugin-name/ ├── .claude-plugin/ # or .plugin/ │ └── plugin.json # Plugin metadata ├── commands/ # Slash commands (optional) ├── agents/ # Specialized agents (optional) ├── skills/ # Agent Skills (optional) ├── hooks/ # Event handlers (optional) │ └── hooks.json ├── .mcp.json # External tool configuration (optional) └── README.md # Plugin documentation

Properties

  • agents: list[AgentDefinition]
  • commands: list[CommandDefinition]
  • description: str Get the plugin description.
  • hooks: HookConfig | None
  • manifest: PluginManifest
  • mcp_config: dict[str, Any] | None
  • name: str Get the plugin name.
  • path: str
  • skills: list[Skill]
  • version: str Get the plugin version.

Methods

add_mcp_config_to()

Add this plugin’s MCP servers to an MCP config. Plugin MCP servers override existing servers with the same name. Merge semantics (Claude Code compatible):
  • mcpServers: deep-merge by server name (last plugin wins for same server)
  • Other top-level keys: shallow override (plugin wins)
  • Parameters: mcp_config – Existing MCP config (or None to create new)
  • Returns: New MCP config dict with this plugin’s servers added

add_skills_to()

Add this plugin’s skills to an agent context. Plugin skills override existing skills with the same name. Includes both explicit skills and command-derived skills.
  • Parameters:
    • agent_context – Existing agent context (or None to create new)
    • max_skills – Optional max total skills (raises ValueError if exceeded)
  • Returns: New AgentContext with this plugin’s skills added
  • Raises: ValueError – If max_skills limit would be exceeded

classmethod fetch()

Fetch a plugin from a remote source and return the local cached path. This method fetches plugins from remote sources (GitHub repositories, git URLs) and caches them locally. Use the returned path with Plugin.load() to load the plugin.
  • Parameters:
    • source Plugin source - can be:
      • Any git URL (GitHub, GitLab, Bitbucket, Codeberg, self-hosted, etc.) e.g., “https://gitlab.com/org/repo”, “git@bitbucket.org:team/repo.git”
      • ”github:owner/repo” - GitHub shorthand (convenience syntax)
      • ”/local/path” - Local path (returned as-is)
    • cache_dir – Directory for caching. Defaults to ~/.openhands/cache/plugins/
    • ref – Optional branch, tag, or commit to checkout.
    • update – If True and cache exists, update it. If False, use cached as-is.
    • repo_path – Subdirectory path within the git repository (e.g., ‘plugins/my-plugin’ for monorepos). Only relevant for git sources, not local paths. If specified, the returned path will point to this subdirectory instead of the repository root.
  • Returns: Path to the local plugin directory (ready for Plugin.load()). If repo_path is specified, returns the path to that subdirectory.
  • Raises: PluginFetchError – If fetching fails or repo_path doesn’t exist.

get_all_skills()

Get all skills including those converted from commands. Returns skills from both the skills/ directory and commands/ directory. Commands are converted to keyword-triggered skills using the format /<plugin-name>:<command-name>.
  • Returns: Combined list of skills (original + command-derived skills).

classmethod load()

Load a plugin from a directory.
  • Parameters: plugin_path – Path to the plugin directory.
  • Returns: Loaded Plugin instance.
  • Raises:
    • FileNotFoundError – If the plugin directory doesn’t exist.
    • ValueError – If the plugin manifest is invalid.

classmethod load_all()

Load all plugins from a directory.
  • Parameters: plugins_dir – Path to directory containing plugin subdirectories.
  • Returns: List of loaded Plugin instances.

model_config = (configuration object)

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class PluginAuthor

Bases: BaseModel Author information for a plugin.

Properties

  • email: str | None
  • name: str

Methods

classmethod from_string()

Parse author from string format ‘Name <email>’.

model_config = (configuration object)

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class PluginManifest

Bases: BaseModel Plugin manifest from plugin.json.

Properties

  • author: PluginAuthor | None
  • description: str
  • model_config: = (configuration object) Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
  • name: str
  • version: str

class PluginSource

Bases: BaseModel Specification for a plugin to load. This model describes where to find a plugin and is used by load_plugins() to fetch and load plugins from various sources.

Examples

>>> # GitHub repository
>>> PluginSource(source="github:owner/repo", ref="v1.0.0")
>>> # Plugin from monorepo subdirectory
>>> PluginSource(
...     source="github:owner/monorepo",
...     repo_path="plugins/my-plugin"
... )
>>> # Local path
>>> PluginSource(source="/path/to/plugin")

Properties

  • ref: str | None
  • repo_path: str | None
  • source: str

Methods

model_config = (configuration object)

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

classmethod validate_repo_path()

Validate repo_path is a safe relative path within the repository.

class ResolvedPluginSource

Bases: BaseModel A plugin source with resolved ref (pinned to commit SHA). Used for persistence to ensure deterministic behavior across pause/resume. When a conversation is resumed, the resolved ref ensures we get exactly the same plugin version that was used when the conversation started. The resolved_ref is the actual commit SHA that was fetched, even if the original ref was a branch name like ‘main’. This prevents drift when branches are updated between pause and resume.

Properties

  • original_ref: str | None
  • repo_path: str | None
  • resolved_ref: str | None
  • source: str

Methods

classmethod from_plugin_source()

Create a ResolvedPluginSource from a PluginSource and resolved ref.

model_config = (configuration object)

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

to_plugin_source()

Convert back to PluginSource using the resolved ref. When loading from persistence, use the resolved_ref to ensure we get the exact same version that was originally fetched.