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
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
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);
|
||||
};
|
||||
Reference in New Issue
Block a user