/**
 * Load MCP server config from the global agent directory.
 *
 * MCPs are user-level integrations for ShortcutXL spreadsheet sessions, not
 * project-local coding config.
 */
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { dirname, join } from 'path';
import { log } from '../../shared/logging/agent-log.js';
const TAG = 'mcp';
const MCP_CONFIG_FILENAME = 'mcp.json';
export function getMcpConfigPath(globalConfigDir) {
    return join(globalConfigDir, MCP_CONFIG_FILENAME);
}
export function readMcpConfigFile(path) {
    if (!existsSync(path))
        return { mcpServers: {} };
    try {
        const raw = readFileSync(path, 'utf-8');
        const parsed = JSON.parse(raw);
        if (!parsed || typeof parsed !== 'object' || !parsed.mcpServers) {
            log.warn(TAG, `MCP config at ${path} missing "mcpServers" key — skipping.`);
            return { mcpServers: {} };
        }
        if (typeof parsed.mcpServers !== 'object' || Array.isArray(parsed.mcpServers)) {
            log.warn(TAG, `MCP config at ${path} has invalid "mcpServers" shape — skipping.`);
            return { mcpServers: {} };
        }
        return { mcpServers: parsed.mcpServers };
    }
    catch (err) {
        const msg = err instanceof SyntaxError ? err.message : String(err);
        log.warn(TAG, `Failed to parse ${path}: ${msg}`);
        return { mcpServers: {} };
    }
}
export function writeMcpConfigFile(path, config) {
    mkdirSync(dirname(path), { recursive: true });
    writeFileSync(path, JSON.stringify(config, null, 2) + '\n', 'utf-8');
}
export function upsertMcpServerConfig(path, name, config, options = {}) {
    const existing = readMcpConfigFile(path);
    if (!options.force && Object.prototype.hasOwnProperty.call(existing.mcpServers, name)) {
        throw new Error(`MCP server '${name}' already exists in ${path}. Choose a different name or rerun with --force to replace it.`);
    }
    existing.mcpServers[name] = config;
    writeMcpConfigFile(path, existing);
}
export function removeMcpServerConfig(path, name) {
    const config = readMcpConfigFile(path);
    if (!Object.prototype.hasOwnProperty.call(config.mcpServers, name)) {
        return false;
    }
    delete config.mcpServers[name];
    writeMcpConfigFile(path, config);
    return true;
}
export function validateServerConfig(name, cfg) {
    if (!cfg || typeof cfg !== 'object') {
        log.warn(TAG, `MCP server '${name}': config must be an object — skipping.`);
        return false;
    }
    const obj = cfg;
    if (typeof obj.command === 'string' && obj.command) {
        return true;
    }
    if (obj.transport === 'streamable_http' && typeof obj.url === 'string' && obj.url) {
        return true;
    }
    log.warn(TAG, `MCP server '${name}': must define either a stdio "command" or hosted "transport":"streamable_http" with "url" — skipping.`);
    return false;
}
function normalizePositiveInt(serverName, fieldName, value) {
    if (value === undefined)
        return undefined;
    if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
        log.warn(TAG, `MCP server '${serverName}': "${fieldName}" must be a positive number — ignoring.`);
        return undefined;
    }
    return Math.floor(value);
}
function normalizeBoolean(serverName, fieldName, value) {
    if (value === undefined)
        return undefined;
    if (typeof value !== 'boolean') {
        log.warn(TAG, `MCP server '${serverName}': "${fieldName}" must be a boolean — ignoring.`);
        return undefined;
    }
    return value;
}
export function normalizeServerConfig(name, cfg) {
    const { timeout: legacyTimeout, startupTimeoutMs: rawStartupTimeoutMs, toolCallTimeoutMs: rawToolCallTimeoutMs, resetToolCallTimeoutOnProgress: rawResetToolCallTimeoutOnProgress, maxToolCallTotalTimeoutMs: rawMaxToolCallTotalTimeoutMs, ...rest } = cfg;
    const startupTimeoutMs = normalizePositiveInt(name, 'startupTimeoutMs', rawStartupTimeoutMs) ??
        normalizePositiveInt(name, 'timeout', legacyTimeout);
    const toolCallTimeoutMs = normalizePositiveInt(name, 'toolCallTimeoutMs', rawToolCallTimeoutMs);
    const maxToolCallTotalTimeoutMs = normalizePositiveInt(name, 'maxToolCallTotalTimeoutMs', rawMaxToolCallTotalTimeoutMs);
    const resetToolCallTimeoutOnProgress = normalizeBoolean(name, 'resetToolCallTimeoutOnProgress', rawResetToolCallTimeoutOnProgress);
    const normalized = {
        ...rest,
        ...(legacyTimeout !== undefined ? { timeout: legacyTimeout } : {}),
        ...(startupTimeoutMs !== undefined ? { startupTimeoutMs } : {}),
        ...(toolCallTimeoutMs !== undefined ? { toolCallTimeoutMs } : {}),
        ...(maxToolCallTotalTimeoutMs !== undefined ? { maxToolCallTotalTimeoutMs } : {}),
        ...(resetToolCallTimeoutOnProgress !== undefined ? { resetToolCallTimeoutOnProgress } : {})
    };
    return normalized;
}
/**
 * Load MCP server configs from the global agent path only.
 */
export function loadMcpConfig(options) {
    const globalPath = getMcpConfigPath(options.globalConfigDir);
    const globalConfig = readMcpConfigFile(globalPath);
    const servers = {};
    for (const [name, cfg] of Object.entries(globalConfig.mcpServers)) {
        if (validateServerConfig(name, cfg)) {
            servers[name] = normalizeServerConfig(name, cfg);
        }
    }
    return servers;
}
//# sourceMappingURL=config.js.map