* IDE-triggered channel enable. Derives the ChannelEntry from the connection's * pluginSource (IDE can't spoof kind/marketplace — we only take the server * name), appends it to session allowedChannels, and runs the full gate. On * gate failure, rolls back the append. On success, registers a notif
( requestId: string, serverName: string, connectionPool: readonly MCPServerConnection[], output: Stream<StdoutMessage>, )
| 4660 | * interactive either.) |
| 4661 | */ |
| 4662 | function handleChannelEnable( |
| 4663 | requestId: string, |
| 4664 | serverName: string, |
| 4665 | connectionPool: readonly MCPServerConnection[], |
| 4666 | output: Stream<StdoutMessage>, |
| 4667 | ): void { |
| 4668 | const respondError = (error: string) => |
| 4669 | output.enqueue({ |
| 4670 | type: 'control_response', |
| 4671 | response: { subtype: 'error', request_id: requestId, error }, |
| 4672 | }) |
| 4673 | |
| 4674 | if (!(feature('KAIROS') || feature('KAIROS_CHANNELS'))) { |
| 4675 | return respondError('channels feature not available in this build') |
| 4676 | } |
| 4677 | |
| 4678 | // Only a 'connected' client has .capabilities and .client to register the |
| 4679 | // handler on. The pool spread at the call site matches mcp_status. |
| 4680 | const connection = connectionPool.find( |
| 4681 | c => c.name === serverName && c.type === 'connected', |
| 4682 | ) |
| 4683 | if (!connection || connection.type !== 'connected') { |
| 4684 | return respondError(`server ${serverName} is not connected`) |
| 4685 | } |
| 4686 | |
| 4687 | const pluginSource = connection.config.pluginSource |
| 4688 | const parsed = pluginSource ? parsePluginIdentifier(pluginSource) : undefined |
| 4689 | if (!parsed?.marketplace) { |
| 4690 | // No pluginSource or @-less source — can never pass the {plugin, |
| 4691 | // marketplace}-keyed allowlist. Short-circuit with the same reason the |
| 4692 | // gate would produce. |
| 4693 | return respondError( |
| 4694 | `server ${serverName} is not plugin-sourced; channel_enable requires a marketplace plugin`, |
| 4695 | ) |
| 4696 | } |
| 4697 | |
| 4698 | const entry: ChannelEntry = { |
| 4699 | kind: 'plugin', |
| 4700 | name: parsed.name, |
| 4701 | marketplace: parsed.marketplace, |
| 4702 | } |
| 4703 | // Idempotency: don't double-append on repeat enable. |
| 4704 | const prior = getAllowedChannels() |
| 4705 | const already = prior.some( |
| 4706 | e => |
| 4707 | e.kind === 'plugin' && |
| 4708 | e.name === entry.name && |
| 4709 | e.marketplace === entry.marketplace, |
| 4710 | ) |
| 4711 | if (!already) setAllowedChannels([...prior, entry]) |
| 4712 | |
| 4713 | const gate = gateChannelServer( |
| 4714 | serverName, |
| 4715 | connection.capabilities, |
| 4716 | pluginSource, |
| 4717 | ) |
| 4718 | if (gate.action === 'skip') { |
| 4719 | // Rollback — only remove the entry we appended. |
no test coverage detected