Skip to main content

What is a Plugin?

A Plugin is a curated bundle of tools from one or more MCP servers (“connectors”). It gives you a single MCP endpoint you can add to your AI client, while still enforcing all the underlying Runlayer policies and access controls. Plugins are useful when you want:
  • A purpose-built toolset for a workflow (e.g. “Release Management”, “Customer Support”, “On-call”)
  • One connection in your MCP client instead of many
  • A safe, shareable setup (private to you or public within your workspace)
Plugins currently expose tools only (no resources or prompts).

Creating a Plugin

1

Open Plugins

Navigate to Plugins in the sidebar.
2

Create a new Plugin

Click Create new and provide a name, optional description, and optional namespace.A namespace (e.g. acme/review) groups related Plugins and ensures a unique (namespace, path) pair. It is optional — leave it blank for one-off Plugins.
3

Choose privacy

  • Private: only you can access it
  • Public: anyone in your workspace can access it
4

Add connectors and pick tools

Select the MCP servers you want, then choose which tools from each server should be included in the Plugin.

Using a Plugin in an MCP client

Open the Plugin and click Add to MCP Client. Runlayer will generate the correct connection details for your selected client.

Managing connectors and tools

  • Add connectors: open a Plugin and click Add Connectors
  • Remove connectors: open the connector menu and choose Remove connector
  • Review tools: expand a connector to see the tools included in the Plugin

Dynamic tools

Dynamic tools are an optimization for Plugins with many tools.

How it works

When Dynamic tools is enabled, the Plugin does not expose every underlying tool definition directly. Instead, it exposes two meta-tools:
  • search_tools: search for the most relevant tools by describing what you want to do (returns tool names + descriptions + input schemas)
  • execute_tool: execute a specific tool by name with arguments (typically chosen from search_tools results)
This creates a two-step flow: search → execute.

Why it’s useful

Sending hundreds of tool definitions (names, descriptions, and JSON schemas) to the LLM can push requests onto a “context route” that:
  • Degrades model performance (more noise in the prompt)
  • Increases latency (more tokens processed)
  • Increases cost (larger prompts and tool schemas)
Dynamic tools keeps the prompt/tooling surface small until the model actually needs a specific tool.

When to use it

  • Enable Dynamic tools for Plugins with many connectors/tools, or when tool schemas are large.
  • Disable it for small Plugins where you want the model to see all tools immediately without an extra search step.

Authoring and syncing with CLI

Runlayer can also publish Claude-format plugins from disk with runlayer plugins push.

Directory structure

my-plugin/
  .claude-plugin/
    plugin.json
  .mcp.json
  skills/
    ticket-triage/
      SKILL.md
      helper.py
    code-review/
      SKILL.md
      prompts.md
  commands/
    review.md
  agents/
    code-reviewer.md
  hooks/
    hooks.json
    validate.sh
  scripts/
    deploy.sh

Manifest

Runlayer uses .claude-plugin/plugin.json as the plugin manifest.
{
  "name": "my-plugin",
  "version": "1.0.0",
  "description": "Review and triage code"
}
FieldRequiredNotes
nameYesUsed as the plugin display name
versionNoStored for discovery
descriptionNoTruncated to 255 characters on push to match the Runlayer API

How files map to Runlayer

  • skills/*/ become individual Runlayer skills
  • supported non-skill files are bundled into one generated root skill named after the plugin
Supported root-skill file types:
  • .md
  • .txt
  • .sh
  • .py
  • .js
  • .ts
  • .json
Excluded from the root skill:
  • .claude-plugin/
  • skills/
  • .mcp.json
  • .lsp.json
  • settings.json
  • root-level README.md
Nested files like agents/README.md are included.

MCP connector extraction

If the plugin has a .mcp.json, Runlayer inspects mcpServers and extracts connectors only from Runlayer proxy URLs:
{
  "mcpServers": {
    "crm": {
      "url": "https://app.runlayer.com/api/v1/proxy/<server-uuid>/mcp"
    }
  }
}
Attached:
  • URLs matching /api/v1/proxy/<server-uuid>/mcp
Skipped:
  • non-Runlayer MCP URLs
  • stdio MCP entries
  • plugin proxy URLs like /api/v1/proxy/plugins/<plugin-uuid>/mcp
Skipped non-Runlayer MCP URLs are shown as warnings during plugins push.

Publishing

runlayer plugins push [PATH] --namespace <namespace> [OPTIONS]
FlagDescription
--namespace, -NRequired. Groups plugins by repo or source namespace
--host, -HRunlayer host URL. Also reads RUNLAYER_HOST
--secret, -sAPI key. Also reads RUNLAYER_API_KEY
--publicPublish the plugin as public
--dynamic-toolsEnable dynamic tools on the plugin
--dry-run, -nShow what would change without making writes
--pruneDelete remote plugins missing locally
PATHRoot directory to scan. Defaults to .
Examples:
# Preview plugin changes
runlayer plugins push --namespace myorg/repo -n

# Push plugins
runlayer plugins push --namespace myorg/repo

# Push and remove remote plugins missing locally
runlayer plugins push --namespace myorg/repo --prune

# Push a single plugin directory
runlayer plugins push ./engineering --namespace myorg/repo

GitHub Actions

A common approach is to sync plugins automatically on every push to main. This keeps your Runlayer workspace in lockstep with your repo without any manual steps. Here’s a starter workflow you can adapt:
name: Push Plugins
on:
  push:
    branches: [main]

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: astral-sh/setup-uv@v4

      - run: uvx runlayer plugins push --namespace ${{ github.repository }} --prune
        env:
          RUNLAYER_HOST: ${{ secrets.RUNLAYER_HOST }}
          RUNLAYER_API_KEY: ${{ secrets.RUNLAYER_API_KEY }}
Set RUNLAYER_HOST and RUNLAYER_API_KEY as repository secrets. The --prune flag removes plugins from Runlayer when their directory is deleted from the repo.

Declarative sync semantics

Plugin skill membership is replaced from local disk.
  • If a local plugin stops including one of its linked skills, that linked skill is removed from the remote plugin
  • standalone skills in the same namespace are not deleted just because their path shares the plugin prefix
  • only skills actually linked to the plugin are considered plugin-managed for deletion
Plugin connector membership is replaced from local .mcp.json.
  • On plugin create, each imported Runlayer connector enables all tools currently visible on that server
  • On plugin update, existing connectors keep their current selected tools
  • On plugin update, newly added connectors enable all tools currently visible on that server
  • If a connector is present locally and resolves to an accessible Runlayer server, it is attached
  • if a connector is missing locally, it is removed remotely on update
  • .mcp.json controls connector membership, not per-tool curation for connectors that already exist remotely
The CLI warns when a push will remove remote connectors not present in the validated local plugin state. --dry-run is a remote-aware preview:
  • it reads remote plugin and skill state
  • it prints created, updated, unchanged, and deleted previews
  • it does not write changes
With --prune, remote plugins not found locally are deleted, along with their linked skills. It does not delete unrelated standalone skills in the same namespace. Warnings you may see:
  • truncated plugin description to 255 characters
  • skipped MCP server <name> with non-Runlayer URL <url>
  • missing server <uuid> skipped
  • push will remove remote connectors not present in local plugin state: <uuid>