> ## Documentation Index
> Fetch the complete documentation index at: https://docs.murmur.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugin marketplaces

> Give every agent in a workspace the same MCP servers and skills from a git repo: what a plugin marketplace is, how to set one up in Murmur, and what each harness supports.

A plugin marketplace is a git repo of plugins that Murmur installs on every agent in a [workspace](https://docs.murmur.dev/concepts/workspaces.md). A plugin bundles MCP servers, and for Claude Code and Codex agents, skills and slash commands too. You maintain the repo, list the plugins you want in the workspace, and each agent starts with them installed.

The example used on this page is public at [`prassoai/example-plugin-market-place`](https://github.com/prassoai/example-plugin-market-place). It publishes one plugin, the Linear MCP server.

## Setup at a glance

1. **Create a marketplace repo** in your GitHub organization and give the Macroscope GitHub App access to it.
2. **Store the MCP server's token** as a Murmur [secret](https://docs.murmur.dev/concepts/secrets.md).
3. **Add the plugin and the secret to your workspace.**
4. **Spawn an agent.** It starts with the plugin's tools.

## What each harness supports

| | Claude Code | Codex | OpenCode |
| - | - | - | - |
| MCP servers (`.mcp.json`) | Yes | Yes | Yes |
| Skills (`skills/`) | Yes | Yes | No |
| Slash commands (`commands/`) | Yes | Yes, as skills | No |
| Other plugin components | As Claude Code's plugin system supports | As Codex's plugin system supports | No |
| MCP auth field | `headers` | `bearer_token_env_var` | `headers` |

## Repo structure

The repo uses the Claude Code plugin format:

```text theme={null}
.claude-plugin/marketplace.json   # marketplace name and plugin list
linear/
  .claude-plugin/plugin.json      # plugin name and version
  .mcp.json                       # MCP servers
  skills/                         # optional: skills (Claude Code and Codex)
  commands/                       # optional: slash commands (Claude Code and Codex)
```

A repo holding a single plugin may skip `marketplace.json` and keep `.claude-plugin/plugin.json` at its root.

### `.claude-plugin/marketplace.json`

Names the marketplace and lists its plugins. Two repos in one workspace cannot use the same marketplace `name`, and each `source` is a path inside the repo.

```json .claude-plugin/marketplace.json theme={null}
{
  "name": "acme",
  "owner": { "name": "Acme", "url": "https://github.com/acme" },
  "plugins": [
    {
      "name": "linear",
      "source": "./linear",
      "description": "Linear MCP server (issues, projects, comments)."
    }
  ]
}
```

### `linear/.claude-plugin/plugin.json`

Describes the plugin. Claude Code and Codex cache plugins by `version`, so bump it every time you change the plugin.

```json linear/.claude-plugin/plugin.json theme={null}
{
  "name": "linear",
  "version": "0.1.0",
  "description": "Linear MCP server.",
  "author": { "name": "Acme", "url": "https://github.com/acme" }
}
```

### `linear/.mcp.json`

Declares the MCP servers. The two highlighted lines carry the same token, because the harnesses read different fields:

```json linear/.mcp.json {6,8} theme={null}
{
  "mcpServers": {
    "linear": {
      "type": "http",
      "url": "https://mcp.linear.app/mcp",
      "bearer_token_env_var": "LINEAR_APPLICATION_TOKEN",
      "headers": {
        "Authorization": "Bearer ${LINEAR_APPLICATION_TOKEN}"
      }
    }
  }
}
```

| Line | Used by | Ignored by |
| - | - | - |
| 6: `"bearer_token_env_var": "LINEAR_APPLICATION_TOKEN"` | **Codex** | Claude Code, OpenCode |
| 8: `"Authorization": "Bearer ${LINEAR_APPLICATION_TOKEN}"` | **Claude Code** and **OpenCode** | Codex |

Keep both lines so the server authenticates on every harness. Both name an environment variable, so the repo holds no token.

## Set it up

**1. Create the repo and give the Macroscope GitHub App access**

Copy the [example repo](https://github.com/prassoai/example-plugin-market-place) into your organization, for example as `acme/plugin-marketplace`, or create the three files above. Commit and push.

If the Macroscope GitHub App has access to **selected repositories** only, open your organization's GitHub settings → **GitHub Apps** → **Macroscope** → **Configure**, and add the repo under **Repository access**.

**2. Store the token as a secret**

Create a [Linear API key](https://linear.app/docs/api-and-webhooks) and store it under the variable name from `.mcp.json`:

```bash theme={null}
read -rsp 'Linear API key: ' LINEAR_KEY; echo
printf '%s' "$LINEAR_KEY" | murmur secret set --scope selected --workspaces my-workspace LINEAR_APPLICATION_TOKEN
```

**3. Add the plugin and the secret to your workspace**

Export the workspace:

```bash theme={null}
murmur get workspace my-workspace > my-workspace.yaml
```

Add a `plugins` entry and mount the secret as an environment variable. Use the field names `murmur get` prints. If the file already has `plugins` or `runtimeSecretMounts`, add the entries to those lists:

```yaml my-workspace.yaml theme={null}
plugins:
  - name: linear
    cloneUrl: https://github.com/acme/plugin-marketplace
    ref: main
runtimeSecretMounts:
  - name: LINEAR_APPLICATION_TOKEN
    mountType: MOUNT_TYPE_ENV
```

Save it:

```bash theme={null}
murmur set workspace my-workspace < my-workspace.yaml
```

In the dashboard, the same fields are under **Settings → Workspaces** → your workspace → **Plugins**. Adding the secret under **Settings → Agent configuration → Secrets** with **Mount automatically** checked adds the mount. See [Add MCP servers to agents](https://docs.murmur.dev/guides/connect-mcp-servers.md) for the dashboard steps.

**4. Spawn an agent**

Plugins install when an agent starts, so spawn a new one:

```bash theme={null}
murmur spawn --workspace my-workspace linear-check "List the Linear teams you can see, using the Linear MCP tools."
```

The agent answers with your Linear teams. On Claude Code the tools are named like `mcp__plugin_linear_linear__list_teams`.

## Workspace `plugins` fields

| Name | Type | Required | Description |
| - | - | - | - |
| `name` | string | yes | The plugin's name in `marketplace.json`, or for a single-plugin repo, the name it installs under. Unique within the workspace. |
| `cloneUrl` | string | yes | HTTPS URL of the repo. |
| `ref` | string | no | Branch, tag, or commit SHA. Empty means the default branch. Entries for the same repo must use the same `ref`. Pin a SHA to decide when a new version reaches your agents. |

## MCP server fields

| Name | Description |
| - | - |
| `type` | `http` or `sse` for a remote server. `stdio`, or omitted with `command` set, for a local server. |
| `url` | Remote server URL. |
| `headers` | HTTP headers. Values may use `${NAME}` environment variables. Read by Claude Code and OpenCode. |
| `bearer_token_env_var` | Environment variable holding a bearer token. Read by Codex. |
| `http_headers`, `env_http_headers` | Fixed headers, and headers filled from named environment variables. Read by Codex. |
| `command`, `args`, `env` | Local server program, arguments, and environment. Refer to files in the plugin as `${CLAUDE_PLUGIN_ROOT}/...`. |

### OpenCode rules

OpenCode loads only a plugin's MCP servers, with these rules:

* `${NAME}` is the only variable form. `${NAME:-default}` makes the plugin unavailable.
* A relative `command` such as `./bin/server` makes the plugin unavailable; write `${CLAUDE_PLUGIN_ROOT}/bin/server`. A bare program name such as `npx` is found on `PATH`.
* A server whose `${NAME}` variable has no value is left out until it has one.
* When two plugins declare the same server name, the plugin listed first in the workspace keeps it.
* `MURMUR_IDENTITY_TOKEN`, `MURMUR_GATEWAY_TOKEN`, and `GH_ENTERPRISE_TOKEN` cannot be referenced.

## Installation

Plugins install each time an agent starts, before its first turn.

A plugin that cannot be installed never holds up the agent: the agent starts with every plugin that did install, and goes without the rest. Agents spawned with [repository confinement](https://docs.murmur.dev/security/repository-confinement.md) are the exception. Their plugin repos count among the repositories they may reach, so if the Macroscope GitHub App cannot access one, the spawn stops with an error naming that repo.

A plugin runs with the agent's full environment, including every mounted secret, so add only repos you trust.

## Errors

| Code | Meaning | What to do |
| - | - | - |
| `INVALID_ARGUMENT` | `plugins[<i>]: ...` when saving the workspace | Give each plugin a unique `name` and an HTTPS `cloneUrl`, and use one `ref` per repo. |
| `INVALID_ARGUMENT` | `repository access cannot be restricted to <repo>: it does not exist or is not covered by this tenant's GitHub App installation` on spawn | A confined agent's workspace lists a plugin repo the Macroscope GitHub App cannot access. Grant the App access, or remove the plugin. |
| `INVALID_ARGUMENT` | `repository access cannot be restricted to "<url>": ... must be a github.com git remote` on spawn | A confined agent's workspace lists a plugin repo outside github.com. Host it on GitHub, or spawn without confinement. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.