Skip to content

Suggest plugins by relevance

Add a relevance block to marketplace entries so Claude Code recommends the right plugin when a session matches, and allowlist the marketplace for suggestions.

If your organisation has a dozen internal plugins, most people will never browse the catalogue to find the one that would help them. Relevance fixes that: you describe, per plugin, what a session looks like when that plugin would be useful, and Claude Code quietly suggests it when it sees a match. A Terraform helper gets offered when someone runs terraform; a design-system plugin gets offered when someone opens the component library repo.

Two people are involved. The marketplace owner adds relevance blocks to entries in marketplace.json. An administrator then allowlists the marketplace in managed settings. Until that second step happens, nobody sees any suggestions from it, whatever the catalogue says.

How suggestions work

Each plugin entry may carry a relevance object containing a topic and a set of signals. Signals are patterns Claude Code checks against what is happening in the current session: the working directory, files Claude has read, commands it has run, and so on.

All matching runs on the user's machine. No network calls are made, and nothing about which signals fired (or their values) is sent to Anthropic or to the marketplace owner.

When a signal matches and the plugin is not installed yet, the suggestion can show up in three places:

WhereWhen
A tip under the spinnerWhile Claude is working on a response. Includes the exact /plugin install command
A one-line notice at session startOnly for cwd matches, before the user has typed anything
The top of the /plugin Discover tabPinned above everything else, labelled with the signal that matched

Nothing is ever installed automatically. The user always makes that call.

Users and projects can switch off the spinner tip and the session-start notice by setting spinnerTipsEnabled to false, or by using spinnerTipsOverride with excludeDefault to replace the built-in tips. Neither setting affects the Discover-tab pin. Both keys are documented in the settings reference.

Writing a relevance block

Here is an entry for a plugin that teaches Claude a team's Kubernetes conventions. It should be suggested when someone runs kubectl or helm, reads a Helm chart, or works inside the platform/k8s folder:

{
  "name": "k8s-conventions",
  "source": "./plugins/k8s-conventions",
  "description": "House rules for manifests, Helm charts and rollout checks",
  "relevance": {
    "topic": "Kubernetes",
    "signals": {
      "cli": ["kubectl", "helm"],
      "filesRead": ["**/Chart.yaml", "**/values*.yaml"],
      "cwd": ["**/platform/k8s"]
    }
  }
}

While none of the signals match, the plugin sits in its normal place in Discover and never appears as a tip.

The relevance object

FieldTypeNotes
topicstringOptional, up to 64 characters. Fills the blank in "Working with topic?". If omitted, it is derived from the plugin name with each hyphen-separated part capitalised. A product name ("Kubernetes") usually reads best; use a domain word like design when the plugin name would sound odd.
signalsobjectThe matchers. At least one must be present or the plugin is never suggested.

Signals

SignalValueWhat it matchesLimits
cwdarray of glob stringsThe session's working directory10 patterns, 256 characters each
cliarray of stringsCommand names from shell commands Claude has run this session. Exact match10 entries, 64 characters each
hostsarray of stringsHostnames from http:// or https:// URLs inside Bash commands this session. Bare lowercase hostname only, no scheme, port or path. Case-insensitive exact match20 entries, 128 characters each
filesReadarray of glob stringsPaths of files Claude has read this session. Forward-slash normalised, case-insensitive10 patterns, 256 characters each
manifestDepsarray of { "file", "pattern" } objectsDependencies declared in package manifests Claude has read. Both values are regular expressions10 entries, each value up to 256 characters. Manifests over 512 KB are skipped

filesRead and manifestDeps also consider files Claude has written or edited in the session, plus the project's auto-loaded CLAUDE.md files.

How cwd matches

cwd is the only signal that can fire at session start. Each pattern is tested against the absolute working directory and, inside a git repository, against the path relative to the repo root as well. Matching is case-insensitive with forward slashes. A pattern covers the directory and everything beneath it, so services/api, services/api/ and services/api/** are equivalent.

How cli matches

For every shell command Claude runs, one command name is recorded: the first word after any leading VAR=value assignments and sudo. Only the first command of a compound line counts. cd deploy && helm upgrade web ./chart records cd, not helm, which is worth knowing when you wonder why a suggestion did not fire.

How manifestDeps matches

Each entry has two JavaScript RegExp source strings:

  • file is tested case-insensitively against the manifest path. Paths are usually absolute, so anchor at the end ($) rather than the start. Separators are not normalised for this signal, so Windows paths contain backslashes.
  • pattern is tested case-sensitively against the file's contents.

This entry fires once Claude has read a Python project file that depends on a company SDK called acme-billing:

{
  "name": "billing-sdk-helper",
  "source": "./plugins/billing-sdk-helper",
  "relevance": {
    "topic": "the billing SDK",
    "signals": {
      "manifestDeps": [
        { "file": "[/\\\\](pyproject\\.toml|requirements[^/\\\\]*\\.txt)$", "pattern": "acme-billing" }
      ]
    }
  }
}

Every regex backslash is doubled because the pattern lives inside a JSON string. [/\\\\] matches either separator and \\. is a literal dot.

Compatibility and limits

Older Claude Code versions ignore fields under relevance and relevance.signals they do not recognise, so adding new signal types does not break them. Exceeding a documented limit on a recognised field is different: it invalidates the whole entry, and users cannot install that plugin until you fix it.

Validating

Run the validator against the marketplace folder before you push:

claude plugin validate ./acme-plugins

For relevance it will, among other things:

  • warn about unknown keys under relevance or relevance.signals;
  • error if relevance is not an object;
  • reject hosts entries that include a scheme, port or path;
  • enforce the size limits above.

Findings are printed with the path of the offending field. The run finishes with Validation passed, Validation passed with warnings or Validation failed.

Allowlisting the marketplace (administrators)

Suggestions only appear for marketplaces named in the managed setting pluginSuggestionMarketplaces. For anything other than Anthropic's official marketplace, the admin must also pin down where that marketplace comes from, either as its entry in extraKnownMarketplaces or as an entry in strictKnownMarketplaces.

If the marketplace is not registered on a machine, or is registered under the allowlisted name but from a different source, no suggestions appear. That source check stops someone registering an unrelated catalogue under a trusted name to get their plugins promoted across your company.

A managed settings file that registers an internal marketplace from GitHub and turns on its suggestions:

{
  "extraKnownMarketplaces": {
    "acme-plugins": {
      "source": { "source": "github", "repo": "acme/claude-plugins" }
    }
  },
  "pluginSuggestionMarketplaces": ["acme-plugins"]
}

The official marketplace can only register from Anthropic's own source, so it needs just its name:

{
  "pluginSuggestionMarketplaces": ["claude-plugins-official"]
}

Manage plugins for your organisation explains extraKnownMarketplaces and strictKnownMarketplaces in full.

What the user actually sees

Using the Kubernetes example, the spinner tip reads:

Working with Kubernetes? Install the k8s-conventions plugin:
/plugin install k8s-conventions@acme-plugins

A cwd match at start-up produces a single line:

plugin suggestion: k8s-conventions@acme-plugins · /plugin

In Discover, the plugin is pinned at the top with a note naming the trigger, such as suggested for this directory or suggested for kubectl commands.

Suggestions are rate limited so they do not become nagging:

  • A given plugin is suggested at most once every three sessions, counting the spinner tip and the start-up notice together.
  • The start-up notice stops for good once the tip and notice have shown the plugin twice in total.
  • Neither appears again once the plugin is installed.
  • The Discover pin happens only the first time the user opens the tab while signals match. That is recorded in ~/.claude.json, and afterwards the plugin appears in its normal position on that machine.

Tip: Keep signals narrow. I would rather a plugin is suggested to the ten people who need it than shown to everyone who ever runs git. Specific CLI names, file globs and manifest dependencies are far better triggers than broad directory patterns.