Docs
Jan Agent
Plugins

Plugins

A plugin is a set of skills installed from a git repository. Any repo that looks like a skill collection - or is a single skill - can be installed with one command, without copying files by hand or trusting a download step. A plugin can also package commands, agent definitions, and optional MCP server declarations. Installing a plugin never runs its code: the install step only clones and moves files, and it does not start a declared MCP server. Enabling one is a separate, explicit setup action that can run local code or contact a remote service. A plugin that declares tools or hooks runs commands later, once a session uses it.

Where they live


~/.jan/projects/<slug>/plugins/
└── release-tools/
├── plugin.toml # optional TOML metadata
├── .claude-plugin/
│ └── plugin.json # Claude-compatible metadata
├── skills/
│ └── prepare/
│ └── SKILL.md
└── ...

A plugin's payload is discovered conventionally, so a repo needs no manifest to be installable:

  • skills/ - the same layout as project skills: folder skills (<name>/SKILL.md) and flat <name>.md
  • SKILL.md at the repo root - a repo that is itself one skill
  • commands/ - slash-invocable prompt templates (<name>.md, nested directories included)
  • agents/ - dispatchable subagent definitions (<name>.md, nested directories included)
  • hooks/hooks.json - optional lifecycle hooks
  • plugin.toml or .claude-plugin/plugin.json - optional metadata: name, description, version, and repo. The directory name is the plugin's identity; the manifest's name is what plugin list shows when present.

Jan discovers these conventional directories in Claude Code-style plugin repositories, including anthropics/claude-plugins-official. Installation does not guarantee runtime compatibility: external commands, credentials, and provider approval may still be required.

Installing

From the command line:


jan plugin install <git-url>
jan plugin install <git-url>#<ref> # install a specific branch or tag
jan plugin install <github-tree-url> # install a plugin from a repo subdirectory
jan plugin install <marketplace-name> # resolve a name through your marketplace
jan plugin list
jan plugin remove <name>
jan plugin search [query]

Inside the interactive agent, the equivalent commands are:


/plugin install <git-url>
/plugin list
/plugin remove <name>
/plugin search <query>

GitHub tree URLs such as https://github.com/anthropics/claude-plugins-official/tree/main/plugins/claude-code-setup are cloned from the repository root and install only the selected subdirectory.

The plugin is cloned (--depth 1), validated to contain a plugin manifest, skills/, commands/, agents/, .mcp.json, or a root SKILL.md, and moved into place under its final name. Reinstalling an existing plugin is refused (remove it first). Specs with shell metacharacters are rejected outright - a plugin name is a name, not a command.

In the TUI, installation continues directly into setup. Jan reads the plugin's .mcp.json or inline mcpServers declarations, collects required keys in a masked field, and offers Enable and connect for each server. Nothing is enabled or executed until you confirm. For providers that accept Jan's OAuth client registration, setup offers browser sign-in and reconnects after successful authorization.

Choose Not now or press Esc to keep the plugin installed without completing its connections. Resume with /plugin setup <name>, or use /plugin setup to choose one installed plugin. Type or paste to filter by name and description (case-insensitive); Backspace edits the search, Up/Down selects, Enter opens setup, and Esc cancels. Rows identify the installation directory and also show the manifest name when it differs. Opening the picker does not run setup for every plugin or enable any servers. No separate terminal command or Jan restart is needed. Plugins without keys or MCP servers need no connection setup.

Imported MCP servers use names such as figma:figma to avoid collisions. Existing servers with different settings are not overwritten; review them in /mcp. Stored key references such as ${API_KEY} and ${CLAUDE_PLUGIN_ROOT} are resolved from the plugin's key store and installed directory. Key files and generated MCP configuration are owner-only (0600) on Unix. Shell credential injection is scoped to the project that registered the keys; starting a run in another project does not replace that project's shell credentials.

OAuth browser sign-in requires successful client registration. If the provider rejects registration, Jan shows the endpoint, HTTP status, and rejection detail in the TUI instead of opening a consent page with a placeholder client ID. Figma restricts its remote MCP server to approved clients (opens in a new tab). Remote Figma sign-in is currently blocked for Jan: registration returns HTTP 403. Installing the Figma plugin does not grant approval, and retrying setup does not remove that restriction. Jan does not currently accept pre-registered OAuth client credentials.

MCP connections already enabled in Jan remain in its user-owned configuration. Skipping setup does not disable them, and removing a plugin does not remove its imported MCP entries. Use /mcp, select the server, then Disable or Remove server. This also applies to placeholder servers declared by example plugins.

A plugin installed or edited outside the running session (another terminal, git pull, a hand edit) is picked up with /reload plugin, which re-scans and reports what was added, removed, or changed without restarting.

Invoking plugin skills

Plugin skills are qualified with their plugin name: release-tools:prepare.

You typeWhat happens
/prepareThe plugin skill, when the name is unambiguous
/skill:release-tools:prepareThe exact plugin skill, always
/release-tools:prepareSame as the explicit form

Precedence: a project skill always wins its short name, and a plain name shared by two plugins is ambiguous, so only the explicit forms work. The slash popup applies the same rules - it offers the short form when unambiguous and the qualified form otherwise. Plugin skills flow through the same invocation mechanics as project skills, including the [skills].enabled whitelist: enable a plugin name (release-tools), one qualified skill (release-tools:prepare), or a plain name.

Plugin commands

A plugin's commands/ directory is discovered as slash commands. Each markdown file is a prompt template: the frontmatter description feeds the slash popup, and the body is loaded into the conversation when you run the command. $ARGUMENTS is replaced with everything you typed after the command name, and $1..$9 with the individual whitespace-separated words. A missing positional becomes empty, and $10-style tokens (and $ARGUMENTATION) are left literal so a body can talk about dollars; the popup only advertises placeholders that will actually be filled:


/feature-dev add user authentication # $ARGUMENTS = "add user authentication", $1 = "add"

Precedence in the slash namespace is built-in command, then plugin command, then skill, so a plugin command named resume never shadows the built-in /resume (the explicit /command:<plugin>:<name> form always works). Like skills, commands honor the [skills].enabled whitelist: a command disabled there is not offered and cannot be fired, even by typing its name directly. The transcript shows one compact [command:<name>] row with your arguments.

Plugin agents

A plugin's agents/ directory is discovered as dispatchable subagents: the markdown frontmatter name and description become the subagent's identity, and the body becomes its system prompt, so a plugin can fan work out to specialized agents exactly as it does in Claude Code. Claude-runtime metadata is handled honestly:

  • model and color are ignored - the agent always runs on the session's model
  • tools is mapped onto Jan's tool names (Read -> read, Glob -> glob, Grep -> grep, WebSearch -> web_search, WebFetch -> web_fetch, TodoWrite -> todo, and so on); names with no Jan equivalent are dropped, and an agent listing only unknown tools inherits the parent's full tool policy
  • skill_list and skill_read stay available even when tools maps to a narrow set - a plugin agent can always load the skills its procedure references, matching Claude Code (see Subagents)

Plugin agents are read-only: create_subagent refuses the plugin scope, and a subagent defined in the project's subagents/ shadows a plugin agent of the same name.

Managing

Interactive commands:


/plugin list # one compact summary line per plugin
/plugin list release-tools # description and skill names for one plugin
/plugin remove release-tools # uninstall
/plugin setup release-tools # resume API-key or MCP connection setup
/plugin search prepare # search the marketplace (name/description)

The compact list shows each plugin's name, version, and payload counts (skills, commands, agents). Descriptions and artifact names are shown only when a plugin name is requested. Interrupted .installing-* staging directories are omitted from listings.

The headless CLI uses the same compact table by default:


jan plugin list --project ~/code/app

Use --json when complete plugin metadata or machine-readable output is needed:


jan plugin list --project ~/code/app --json

Install and search continue to print JSON results:


jan plugin install https://github.com/acme/release-tools --project ~/code/app
jan plugin search prepare --project ~/code/app

Removal is a directory delete under the same name guard as everything else in the agent: a name can never escape the plugins directory.

Plugin tools and hooks

Beyond instructions, a plugin can carry two executable payloads. Both are covered in full on the hooks page; the short form:

  • [[tools]] in plugin.toml declares a tool. Each entry names a description, a JSON Schema in [tools.parameters], and a command. The model calls it as plugin__<plugin>__<tool>, the arguments arrive on the command's stdin as JSON, and its stdout becomes the tool result. Plugin tools are gated like MCP tools: prompted rather than auto-approved, and withheld in Plan mode.
  • hooks/hooks.json declares lifecycle hooks - commands that run before and after tool calls, at prompt submit, at session start and end, and before a compaction. A PreToolUse hook can deny a call outright.

Both are on by default and both switch off in agent.toml without uninstalling the plugin:


[plugins]
hooks = false
tools = false

⚠️

Installing a plugin that ships hooks means a third party's commands run on your tool calls, with whatever access the run has. Read its hooks/hooks.json and [[tools]] before installing, or turn them off and keep only its skills.

Marketplace

Point [plugins] marketplace in agent.toml at a JSON index to install by name and search:


[plugins]
marketplace = "https://example.com/jan-plugins/index.json"


[
{ "name": "release-tools", "description": "Release automation", "repo": "https://github.com/acme/release-tools" },
{ "name": "triage", "description": "Issue triage helpers", "repo": "https://github.com/acme/triage", "ref": "main" }
]

repo is cloned with an optional pinned ref (branch or tag). Without a configured marketplace, install <name> and search report the missing config - direct git URLs always work.

Plugins are shared with Jan Desktop - the same project files, through the same directory. The desktop agent exposes agent_plugin_list, agent_plugin_install, agent_plugin_remove, and agent_plugin_search for UI integration.