Files
openchamber/packages/web/server/lib/opencode/managed-plugin-config.js
T
Bohdan Triapitsyn ce5c0c068e feat(mcp): reconnect failed MCP servers in managed OpenCode
OpenCode marks an MCP server failed when it does not come up at startup
or when a live connection drops, and never retries. A new managed plugin
reconnects those servers with a per-server backoff (1s doubling to a 30s
cap), wakes early on mcp.tools.changed, and stops on dispose. Only the
failed state is retried; disabled and auth states stay untouched.

The OPENCODE_CONFIG_CONTENT merge that agent-tool and system-prompt each
carried is now one shared helper so the three managed plugins compose.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
2026-09-04 22:56:27 +03:00

36 lines
1.7 KiB
JavaScript

import { parse as parseJsonc } from 'jsonc-parser';
const isJsonObject = (value) => value !== null && value !== undefined && Object.getPrototypeOf(value) === Object.prototype;
/**
* Append a managed OpenChamber plugin to the `plugin` list of an
* `OPENCODE_CONFIG_CONTENT` value.
*
* Existing entries are preserved; an earlier entry for the same URL is dropped
* so a restart never registers the same plugin twice. Every managed plugin
* (agent tools, system prompt optimizer, MCP reconnect) goes through this one
* merge so they compose in any order.
*
* @param {string | undefined} rawConfig the current `OPENCODE_CONFIG_CONTENT`, JSONC or unset
* @param {string} pluginUrl `file://` URL of the materialized plugin
* @param {string} purpose short noun phrase for the error message, e.g. "managed tool"
* @returns {string} the merged config as JSON
*/
export const appendManagedPlugin = (rawConfig, pluginUrl, purpose) => {
const errors = [];
const text = (rawConfig ?? '').trim();
const parsed = text ? parseJsonc(text, errors, { allowTrailingComma: true }) : {};
if (errors.length > 0 || !isJsonObject(parsed)) {
throw new Error(`OPENCODE_CONFIG_CONTENT must contain a valid JSON object before OpenChamber can inject its ${purpose}`);
}
if (parsed.plugin !== undefined && !Array.isArray(parsed.plugin)) {
throw new Error(`OPENCODE_CONFIG_CONTENT plugin must be an array before OpenChamber can inject its ${purpose}`);
}
const configured = Array.isArray(parsed.plugin) ? parsed.plugin : [];
parsed.plugin = [
...configured.filter((value) => value !== pluginUrl && (!Array.isArray(value) || value[0] !== pluginUrl)),
pluginUrl,
];
return JSON.stringify(parsed);
};