--dangerously-load-development-channels does not register a channel; only allowedChannelPlugins works

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 1 comment · opened Jul 31, 2026

--dangerously-load-development-channels does not register a channel; only allowedChannelPlugins works

Summary

Per the channels reference, a channel plugin under development should be loadable with:

claude --dangerously-load-development-channels plugin:yourplugin@yourmarketplace

On 2.1.220 this flag has no observable effect. The channel is never registered, no error surfaces, and the documented confirmation dialog never appears. The only way we could get a non-allowlisted channel to register was an admin allowedChannelPlugins entry in /etc/claude-code/managed-settings.json — which the docs present as an enterprise alternative, not a prerequisite for local development.

Net effect: there appears to be no working path for developing a channel plugin without root access to the machine.

Environment

  • Claude Code 2.1.220 (native installer)
  • Linux (kernel 6.8), bash
  • Plugin installed from a local directory marketplace, --scope local, confirmed enabled
  • MCP server declares capabilities.experimental['claude/channel'] and connects successfully

Reproduction

Uses only first-party code, so no third-party plugin is needed. The point is to give fakechat a non-allowlisted identity.

  1. Copy external_plugins/fakechat from anthropics/claude-plugins-official into a scratch marketplace, renaming both the plugin and the marketplace so it is no longer on the approved allowlist:

``
ctl-mp/
.claude-plugin/marketplace.json → { "name": "probe-mp", "plugins": [{ "name": "notchat", "source": "./plugins/notchat" }] }
plugins/notchat/ → verbatim fakechat, with plugin.json name → "notchat"
and .mcp.json server key → "notchat"
``

  1. Install it and launch with the documented development flag:

``bash
claude plugin marketplace add ./ctl-mp
claude plugin install notchat@probe-mp --scope local
claude --debug --dangerously-load-development-channels plugin:notchat@probe-mp
``

  1. Read ~/.claude/debug/<session-id>.txt.

Expected

The channel registers. Per the docs, a full-screen warning dialog appears first ("I am using this for local development"), then a startup notice reading messages from plugin:notchat@probe-mp inject directly in this session.

Actual

The MCP server starts and connects normally (fakechat: http://localhost:8799 on stderr, tools discovered), but:

MCP server "plugin:notchat:notchat": Channel notifications skipped:
  server plugin:notchat:notchat not in --channels list for this session

No warning dialog appears. No startup notice appears. Nothing is written to the session or to stderr — the failure is only visible under --debug.

What we tried

All on 2.1.220, against both the fakechat clone above and a separate third-party channel plugin, across entry forms server:x, plugin:x:x, and plugin:x@marketplace, in default and bypassPermissions permission modes:

| Launch | Debug-log verdict |
|---|---|
| --dangerously-load-development-channels plugin:x@mp | not in --channels list for this session |
| --channels plugin:x@mp + --dangerously-load-development-channels plugin:x@mp | is not on the approved channels allowlist (use --dangerously-load-development-channels for local dev) |
| --channels plugin:x@mp, with allowedChannelPlugins set | Channel notifications registered ✅ |
| --dangerously-load-development-channels plugin:x@mp, with allowedChannelPlugins set | not in --channels list for this session |

Two things stand out:

  1. The second row is self-contradicting. Claude Code advises using --dangerously-load-development-channels in the same message produced by a session that was passed exactly that flag with exactly that value.
  2. The flag never enters its entry into the channels list. Row 1 fails with "not in --channels list", so the flag does not imply enablement. But rows 2 and 4 show it does not bypass the allowlist either — so it does not appear to do anything at all. Removing the allowedChannelPlugins entry breaks registration again even with the flag present.

Workaround

Add the plugin to /etc/claude-code/managed-settings.json and use --channels alone:

{"channelsEnabled":true,
 "allowedChannelPlugins":[{"marketplace":"your-marketplace","plugin":"yourplugin"}]}

This requires root and, since the list replaces the default Anthropic allowlist rather than extending it, every channel plugin already in use must be re-listed or it silently stops working.

Secondary: a manually-configured MCP server silently shadows a plugin's channel

Related, and it cost us several hours before we found it. If an .mcp.json or --mcp-config entry happens to use the same server name as a plugin's own server, the plugin's server is suppressed:

Suppressing plugin MCP server "plugin:msteams:msteams": duplicates manually-configured "msteams"

The surviving server is then server:msteams, so a --channels plugin:msteams@marketplace entry can never match it, and the channel silently fails to register. Both lines are [DEBUG]-level, so with no --debug the symptom is simply that inbound events vanish.

Two smaller notes in the same area:

  • The Channels (experimental) startup banner prints even when registration was skipped, so it is not a usable signal that a channel is live.
  • --plugin-dir yields the server id plugin:<name>:<name> with no marketplace component, so a plugin:<name>@<marketplace> entry can never match a plugin loaded that way. Worth documenting, since --plugin-dir is otherwise the natural choice while developing.

Suggested fixes

  • Make --dangerously-load-development-channels actually register its entries, or emit a real error when it declines to.
  • Never advise a flag in a message produced by a session that was already given that flag.
  • Promote the "channel skipped" lines from [DEBUG] to a visible warning — a silently dead channel is very expensive to diagnose.
  • Document that allowedChannelPlugins replaces rather than extends the default allowlist.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗