* 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>, )
| 4895 | * interactive either.) |
| 4896 | */ |
| 4897 | function handleChannelEnable( |
| 4898 | requestId: string, |
| 4899 | serverName: string, |
| 4900 | connectionPool: readonly MCPServerConnection[], |
| 4901 | output: Stream<StdoutMessage>, |
| 4902 | ): void { |
| 4903 | const respondError = (error: string) => |
| 4904 | output.enqueue({ |
| 4905 | type: 'control_response', |
| 4906 | response: { subtype: 'error', request_id: requestId, error }, |
| 4907 | }) |
| 4908 | |
| 4909 | if (!(feature('KAIROS') || feature('KAIROS_CHANNELS'))) { |
| 4910 | return respondError('channels feature not available in this build') |
| 4911 | } |
| 4912 | |
| 4913 | // Only a 'connected' client has .capabilities and .client to register the |
| 4914 | // handler on. The pool spread at the call site matches mcp_status. |
| 4915 | const connection = connectionPool.find( |
| 4916 | c => c.name === serverName && c.type === 'connected', |
| 4917 | ) |
| 4918 | if (!connection || connection.type !== 'connected') { |
| 4919 | return respondError(`server ${serverName} is not connected`) |
| 4920 | } |
| 4921 | |
| 4922 | const pluginSource = connection.config.pluginSource |
| 4923 | const parsed = pluginSource ? parsePluginIdentifier(pluginSource) : undefined |
| 4924 | if (!parsed?.marketplace) { |
| 4925 | // No pluginSource or @-less source — can never pass the {plugin, |
| 4926 | // marketplace}-keyed allowlist. Short-circuit with the same reason the |
| 4927 | // gate would produce. |
| 4928 | return respondError( |
| 4929 | `server ${serverName} is not plugin-sourced; channel_enable requires a marketplace plugin`, |
| 4930 | ) |
| 4931 | } |
| 4932 | |
| 4933 | const entry: ChannelEntry = { |
| 4934 | kind: 'plugin', |
| 4935 | name: parsed.name, |
| 4936 | marketplace: parsed.marketplace, |
| 4937 | } |
| 4938 | // Idempotency: don't double-append on repeat enable. |
| 4939 | const prior = getAllowedChannels() |
| 4940 | const already = prior.some( |
| 4941 | e => |
| 4942 | e.kind === 'plugin' && |
| 4943 | e.name === entry.name && |
| 4944 | e.marketplace === entry.marketplace, |
| 4945 | ) |
| 4946 | if (!already) setAllowedChannels([...prior, entry]) |
| 4947 | |
| 4948 | const gate = gateChannelServer( |
| 4949 | serverName, |
| 4950 | connection.capabilities, |
| 4951 | pluginSource, |
| 4952 | ) |
| 4953 | if (gate.action === 'skip') { |
| 4954 | // Rollback — only remove the entry we appended. |
no test coverage detected