"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderMcpToolsetManualDetailsSection = renderMcpToolsetManualDetailsSection;
exports.readMcpToolsetMappingSnapshot = readMcpToolsetMappingSnapshot;
exports.renderMcpToolsetMappingSection = renderMcpToolsetMappingSection;
exports.renderMcpToolsetSetupGuideSection = renderMcpToolsetSetupGuideSection;
exports.renderMcpManual = renderMcpManual;
exports.measureRenderedTeamMgmtMcpTopicRawChars = measureRenderedTeamMgmtMcpTopicRawChars;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const config_1 = require("../mcp/config");
const manual_problems_1 = require("../mcp/manual-problems");
const registry_1 = require("./registry");
const MINDS_DIR = '.minds';
const TEAM_YAML_REL = `${MINDS_DIR}/team.yaml`;
const MCP_YAML_REL = `${MINDS_DIR}/mcp.yaml`;
function fmtHeader(title) {
    return `# ${title}\n`;
}
function fmtSubHeader(title) {
    return `\n## ${title}\n`;
}
function fmtList(items) {
    return (items
        .filter((s) => s.trim() !== '')
        .map((s) => `- ${s}`)
        .join('\n') + '\n');
}
function fmtCodeBlock(lang, lines) {
    const body = lines.join('\n');
    return `\n\n\`\`\`${lang}\n${body}\n\`\`\`\n`;
}
function isFsErrWithCode(err) {
    return typeof err === 'object' && err !== null && 'code' in err;
}
function firstNonEmptyLine(raw) {
    for (const line of raw.split('\n')) {
        const trimmed = line.trim();
        if (trimmed !== '')
            return trimmed;
    }
    return raw.trim();
}
function describeMcpManualState(language, state) {
    if (state.kind === 'present') {
        const contentText = state.manual.content && state.manual.content.trim() !== ''
            ? language === 'zh'
                ? '有'
                : 'yes'
            : language === 'zh'
                ? '无'
                : 'no';
        const contentFileText = state.manual.contentFile && state.manual.contentFile.trim() !== ''
            ? state.manual.contentFile
            : language === 'zh'
                ? '无'
                : 'no';
        return language === 'zh'
            ? `手册=已配置（contentFile=${contentFileText}，content=${contentText}，sections=${state.manual.sections.length}）`
            : `manual=configured (contentFile=${contentFileText}, content=${contentText}, sections=${state.manual.sections.length})`;
    }
    if (state.kind === 'invalid') {
        return language === 'zh'
            ? `手册=声明无效（${firstNonEmptyLine(state.errorText)}；不影响 toolset 可用性，但这是团队管理配置问题）`
            : `manual=invalid declaration (${firstNonEmptyLine(state.errorText)}; toolset availability is unaffected, but team management should fix this)`;
    }
    return language === 'zh'
        ? '手册=未配置（不影响 toolset 可用性；标准工具说明与参数来自 MCP tools/list）'
        : 'manual=not configured (toolset availability is unaffected; standard tool descriptions/arguments come from MCP tools/list)';
}
function renderMcpManualPresentBlock(language, serverId, manual) {
    let out = `\n### toolset \`${serverId}\`\n`;
    if (manual.contentFile && manual.contentFile.trim() !== '') {
        out += fmtList([
            language === 'zh'
                ? `手册文件前缀（\`contentFile\`）：\`${manual.contentFile}\``
                : `Manual file prefix (\`contentFile\`): \`${manual.contentFile}\``,
        ]);
        if ((!manual.content || manual.content.trim() === '') && manual.sections.length === 0) {
            out += fmtList([
                language === 'zh'
                    ? `该 toolset 的最终 \`man({ "toolsetId": "${serverId}" })\` 正文会从该前缀下的 topic 文件加载；这里不直接内嵌正文。`
                    : `The final \`man({ "toolsetId": "${serverId}" })\` body is loaded from topic files under this prefix; it is not inlined here.`,
            ]);
        }
    }
    if (manual.content && manual.content.trim() !== '') {
        out +=
            fmtList([language === 'zh' ? '总说明（content）：' : 'Overview (`content`):']) +
                fmtCodeBlock('markdown', [manual.content]);
    }
    else {
        out += fmtList([
            language === 'zh' ? '总说明（content）：（未提供）' : 'Overview (`content`): (not provided)',
        ]);
    }
    if (manual.sections.length > 0) {
        for (const section of manual.sections) {
            out +=
                fmtList([language === 'zh' ? `章节：${section.title}` : `Section: ${section.title}`]) +
                    fmtCodeBlock('markdown', [section.content]);
        }
    }
    else {
        out += fmtList([
            language === 'zh' ? '章节（sections）：（未提供）' : 'Sections (`sections`): (not provided)',
        ]);
    }
    return out;
}
function inferAutoGeneratedMcpIntentDirections(language, toolNames) {
    const names = toolNames.map((name) => name.toLowerCase());
    const out = [];
    const pushUnique = (line) => {
        if (!out.includes(line))
            out.push(line);
    };
    const has = (needle) => names.some((name) => name.includes(needle));
    if (has('browser') || has('page') || has('playwright') || has('screenshot')) {
        pushUnique(language === 'zh'
            ? '网页/浏览器自动化：用于页面操作、采样、信息抓取与可视化回归辅助。'
            : 'Web/browser automation: page actions, sampling, information extraction, and visual-regression assistance.');
    }
    if (has('git') || has('repo') || has('diff') || has('pr') || has('commit')) {
        pushUnique(language === 'zh'
            ? '仓库协作与变更分析：用于读取仓库状态、比较差异、辅助代码审查。'
            : 'Repository collaboration and change analysis: inspect repo state, compare diffs, and support review workflows.');
    }
    if (has('sql') || has('db') || has('query') || has('table') || has('postgres')) {
        pushUnique(language === 'zh'
            ? '数据查询/诊断：用于结构化检索与数据一致性排查。'
            : 'Data query/diagnostics: structured retrieval and data-consistency investigation.');
    }
    if (has('k8s') || has('kubectl') || has('deploy') || has('cluster') || has('helm')) {
        pushUnique(language === 'zh'
            ? '部署与运行状态诊断：用于集群状态检查、发布过程观测与故障定位。'
            : 'Deployment/runtime diagnostics: cluster-state checks, release observation, and incident localization.');
    }
    if (has('ticket') || has('issue') || has('jira') || has('linear')) {
        pushUnique(language === 'zh'
            ? '任务流转协作：用于工单读取、状态同步与流程追踪。'
            : 'Ticket/workflow collaboration: ticket lookup, state synchronization, and process tracking.');
    }
    if (out.length === 0) {
        out.push(language === 'zh'
            ? '未识别出明确领域特征：建议按工具 description/参数逐个确认用途，并与人类用户共同定义边界。'
            : 'No clear domain signature detected: review each tool description/arguments and define boundaries jointly with the human user.');
    }
    return out;
}
function formatLoadedToolNamesPreview(language, entry) {
    const preview = entry.loadedToolNamesPreview ?? [];
    if (preview.length === 0) {
        return language === 'zh' ? '（无）' : '(none)';
    }
    const suffix = entry.loadedToolCount !== undefined && entry.loadedToolCount > preview.length ? ', ...' : '';
    return `${preview.join(', ')}${suffix}`;
}
function renderRawMcpAutomaticBaselineSection(language, entry) {
    const lines = [];
    const statusText = entry.status === 'registered'
        ? language === 'zh'
            ? '已加载'
            : 'loaded'
        : entry.status === 'disabled'
            ? language === 'zh'
                ? '已禁用'
                : 'disabled'
            : entry.status === 'declared_unloaded'
                ? language === 'zh'
                    ? '已声明但未加载'
                    : 'declared but not loaded'
                : language === 'zh'
                    ? '声明无效'
                    : 'invalid declaration';
    const transportText = entry.transport === 'invalid'
        ? 'invalid'
        : entry.transport === 'unknown'
            ? 'unknown'
            : entry.transport === 'stdio'
                ? 'stdio'
                : 'http';
    lines.push(`\n### toolset \`${entry.serverId}\``);
    lines.push(...fmtList([
        language === 'zh'
            ? '未配置 `servers.<serverId>.manual`；以下为标准 Raw MCP 接入的自动基础说明。'
            : '`servers.<serverId>.manual` is not configured; below is the automatic baseline for this standard Raw MCP integration.',
        language === 'zh'
            ? `当前连接状态：status=${statusText}，transport=${transportText}。`
            : `Current connection status: status=${statusText}, transport=${transportText}.`,
        language === 'zh'
            ? '说明：MCP 工具名称、description 与参数 schema 已由 server 通过 `tools/list` 自动提供，并随工具函数说明进入 LLM 上下文；手册应列出工具名清单以澄清“哪些工具属于该 toolset”，但不要复制参数 schema 或长篇工具契约。'
            : 'Note: MCP tool names, descriptions, and parameter schemas are already provided by the server through `tools/list` and enter LLM context as function-tool metadata; manuals should list tool names to clarify which tools belong to this toolset, but should not copy parameter schemas or long tool-contract prose.',
    ])
        .trimEnd()
        .split('\n'));
    lines.push(...fmtList([language === 'zh' ? '章节：工具契约来源' : 'Section: Tool contract source'])
        .trimEnd()
        .split('\n'));
    lines.push(...fmtList([
        language === 'zh'
            ? entry.status === 'disabled'
                ? '该 server 当前为 disabled，按设计暴露为 0 工具 toolset；启用后请查看各工具函数说明获取工具契约。需要启用时运行 `mcp_restart`。'
                : '工具契约详见各工具函数说明（以及 WebUI 工具列表）。这些信息来自 MCP `tools/list`；manual 应维护工具名归属清单与团队使用边界，不要重复维护参数 schema。'
            : entry.status === 'disabled'
                ? 'This server is disabled and intentionally exposed as a zero-tool toolset; after enabling it, use the individual function-tool descriptions for the tool contract. Run `mcp_restart` to enable it.'
                : 'See the individual function-tool descriptions (and the WebUI tools list) for tool contracts. This information comes from MCP `tools/list`; the manual should maintain tool-name attribution and team usage boundaries, not duplicate parameter schemas.',
        language === 'zh'
            ? `当前工具名预览（最多展示前 6 个）：${formatLoadedToolNamesPreview(language, entry)}`
            : `Current tool-name preview (up to first 6): ${formatLoadedToolNamesPreview(language, entry)}`,
    ])
        .trimEnd()
        .split('\n'));
    lines.push(...fmtList([
        language === 'zh'
            ? '章节：主要意图方向（自动提示）'
            : 'Section: Primary intent directions (automatic hints)',
    ])
        .trimEnd()
        .split('\n'));
    lines.push(...fmtList(inferAutoGeneratedMcpIntentDirections(language, entry.loadedToolNames ?? []))
        .trimEnd()
        .split('\n'));
    lines.push(...fmtList([
        language === 'zh'
            ? '建议团队增强：即使该 MCP 可裸用，仍建议与人类用户确认整体定位与边界，并把简短说明写入 `servers.<serverId>.manual`；手册中必须列出工具名清单以澄清归属关系，但不要复制参数说明。'
            : 'Recommended team enhancement: even though this MCP can be used raw, confirm the overall positioning and boundaries with the human user and write a short note into `servers.<serverId>.manual`; the manual must list tool names to clarify attribution, but should not copy parameter docs.',
    ])
        .trimEnd()
        .split('\n'));
    return lines.join('\n') + '\n';
}
function renderMcpToolsetManualDetailsSection(language, snapshot) {
    const header = language === 'zh'
        ? fmtSubHeader('MCP toolset 手册（来自 `.minds/mcp.yaml` 的 `servers.<serverId>.manual`）')
        : fmtSubHeader('MCP Toolset Manuals (from `.minds/mcp.yaml` `servers.<serverId>.manual`)');
    if (snapshot.kind !== 'loaded')
        return '';
    const entries = snapshot.entries;
    if (entries.length === 0) {
        return (header +
            fmtList([
                language === 'zh'
                    ? '当前没有 MCP toolset 可展示。'
                    : 'There are currently no MCP toolsets to display.',
            ]));
    }
    let out = header +
        fmtList([
            language === 'zh'
                ? '若 `servers.<serverId>.manual` 已配置，这里会展示其 inline 说明或 `contentFile` 路径信息；若未配置，则展示基于 MCP `tools/list` 的 Raw MCP 自动基线信息与可选意图提示。'
                : 'If `servers.<serverId>.manual` is configured, this section shows the inline guidance and/or the `contentFile` path information; if not configured, it shows Raw MCP automatic baseline information from MCP `tools/list` plus optional intent hints.',
        ]);
    for (const entry of entries) {
        if (entry.manualState.kind === 'missing') {
            out += renderRawMcpAutomaticBaselineSection(language, entry);
            continue;
        }
        if (entry.manualState.kind === 'invalid') {
            out += `\n### toolset \`${entry.serverId}\`\n`;
            out += fmtList([
                language === 'zh'
                    ? `\`servers.${entry.serverId}.manual\` 声明无效：${firstNonEmptyLine(entry.manualState.errorText)}`
                    : `\`servers.${entry.serverId}.manual\` is invalid: ${firstNonEmptyLine(entry.manualState.errorText)}`,
                language === 'zh'
                    ? '这不影响 toolset 可用性；请根据工具说明继续使用，并通知团队管理者修复手册字段。'
                    : 'Toolset availability is unaffected; continue by tool-level descriptions and ask the team manager to fix the manual field.',
            ]);
            continue;
        }
        if (entry.manualState.kind !== 'present')
            continue;
        const manual = entry.manualState.manual;
        out += renderMcpManualPresentBlock(language, entry.serverId, manual);
    }
    return out;
}
async function readMcpToolsetMappingSnapshot() {
    const mcpAbsPath = path_1.default.resolve(process.cwd(), MCP_YAML_REL);
    let rawText;
    try {
        rawText = await promises_1.default.readFile(mcpAbsPath, 'utf8');
    }
    catch (err) {
        if (isFsErrWithCode(err) && err.code === 'ENOENT') {
            return { kind: 'missing' };
        }
        return {
            kind: 'invalid_yaml',
            errorText: err instanceof Error ? err.message : String(err),
        };
    }
    const parsed = (0, config_1.parseMcpYaml)(rawText);
    if (!parsed.ok) {
        return { kind: 'invalid_yaml', errorText: parsed.errorText };
    }
    const manualInfo = (0, manual_problems_1.parseMcpManualByServer)(rawText);
    const registeredToolsets = (0, registry_1.listToolsets)();
    const invalidByServerId = new Map();
    for (const invalid of parsed.invalidServers) {
        invalidByServerId.set(invalid.serverId, invalid.errorText);
    }
    const disabledServerIds = new Set(parsed.disabledServerIdsInYamlOrder);
    const entries = [];
    for (const serverId of parsed.serverIdsInYamlOrder) {
        const invalidError = invalidByServerId.get(serverId);
        const manualError = manualInfo.invalidByServerId.get(serverId);
        const manual = manualInfo.manualByServerId.get(serverId);
        const manualState = manualError
            ? { kind: 'invalid', errorText: manualError }
            : manual
                ? { kind: 'present', manual }
                : { kind: 'missing' };
        if (invalidError) {
            entries.push({
                serverId,
                transport: 'invalid',
                status: 'declared_invalid',
                errorText: invalidError,
                manualState,
            });
            continue;
        }
        if (disabledServerIds.has(serverId)) {
            entries.push({
                serverId,
                transport: 'unknown',
                status: 'disabled',
                loadedToolCount: 0,
                loadedToolNames: [],
                loadedToolNamesPreview: [],
                manualState,
            });
            continue;
        }
        const transport = parsed.config.servers[serverId]?.transport ?? 'invalid';
        const loadedTools = registeredToolsets[serverId];
        if (loadedTools) {
            const loadedToolNames = loadedTools.map((t) => t.name);
            entries.push({
                serverId,
                transport,
                status: 'registered',
                loadedToolCount: loadedTools.length,
                loadedToolNames,
                loadedToolNamesPreview: loadedToolNames.slice(0, 6),
                manualState,
            });
            continue;
        }
        entries.push({
            serverId,
            transport,
            status: 'declared_unloaded',
            manualState,
        });
    }
    return { kind: 'loaded', entries };
}
function renderMcpToolsetMappingSection(language, snapshot) {
    const header = language === 'zh'
        ? fmtSubHeader('MCP serverId -> toolset 当前映射（动态读取 .minds/mcp.yaml）')
        : fmtSubHeader('Current MCP serverId -> toolset mapping (from .minds/mcp.yaml)');
    if (snapshot.kind === 'missing') {
        const items = language === 'zh'
            ? [
                `未发现 \`${MCP_YAML_REL}\`；当前没有可映射的 MCP toolset。`,
                `设置方法：在 \`${MCP_YAML_REL}\` 增加 \`servers.<serverId>\`，该 \`serverId\` 会映射为同名 toolset。`,
            ]
            : [
                `\`${MCP_YAML_REL}\` not found; there are currently no MCP toolsets to map.`,
                `Setup rule: add \`servers.<serverId>\` in \`${MCP_YAML_REL}\`; that \`serverId\` maps to a toolset with the same name.`,
            ];
        return header + fmtList(items);
    }
    if (snapshot.kind === 'invalid_yaml') {
        const items = language === 'zh'
            ? [
                `\`${MCP_YAML_REL}\` 解析失败，暂时无法生成 serverId -> toolset 映射。`,
                `错误：${firstNonEmptyLine(snapshot.errorText)}`,
                '先运行 `team_mgmt_validate_mcp_cfg({})` 修复配置，再查看映射。',
            ]
            : [
                `Failed to parse \`${MCP_YAML_REL}\`; cannot build serverId -> toolset mapping yet.`,
                `Error: ${firstNonEmptyLine(snapshot.errorText)}`,
                'Run `team_mgmt_validate_mcp_cfg({})` first, then re-check mapping.',
            ];
        return header + fmtList(items);
    }
    if (snapshot.entries.length === 0) {
        const items = language === 'zh'
            ? [
                `\`${MCP_YAML_REL}\` 已存在，但 \`servers\` 为空；当前没有 MCP toolset。`,
                '添加 `servers.<serverId>` 后，对应 `serverId` 会映射为同名 toolset。',
            ]
            : [
                `\`${MCP_YAML_REL}\` exists but \`servers\` is empty; there are currently no MCP toolsets.`,
                'After adding `servers.<serverId>`, that `serverId` maps to a same-name toolset.',
            ];
        return header + fmtList(items);
    }
    const lines = [];
    for (const entry of snapshot.entries) {
        const transportText = entry.transport === 'invalid'
            ? 'invalid'
            : entry.transport === 'unknown'
                ? 'unknown'
                : entry.transport === 'stdio'
                    ? 'stdio'
                    : 'http';
        const manualText = describeMcpManualState(language, entry.manualState);
        if (entry.status === 'registered') {
            const previewText = formatLoadedToolNamesPreview(language, entry);
            lines.push(language === 'zh'
                ? `\`servers.${entry.serverId}\` -> toolset \`${entry.serverId}\`（transport=${transportText}，状态=已加载，tools=${entry.loadedToolCount ?? 0}：${previewText}；${manualText}）`
                : `\`servers.${entry.serverId}\` -> toolset \`${entry.serverId}\` (transport=${transportText}, status=loaded, tools=${entry.loadedToolCount ?? 0}: ${previewText}; ${manualText})`);
            continue;
        }
        if (entry.status === 'declared_unloaded') {
            lines.push(language === 'zh'
                ? `\`servers.${entry.serverId}\` -> toolset \`${entry.serverId}\`（transport=${transportText}，状态=已声明但未加载；${manualText}）`
                : `\`servers.${entry.serverId}\` -> toolset \`${entry.serverId}\` (transport=${transportText}, status=declared but not loaded; ${manualText})`);
            continue;
        }
        if (entry.status === 'disabled') {
            lines.push(language === 'zh'
                ? `\`servers.${entry.serverId}\` -> toolset \`${entry.serverId}\`（状态=已禁用，tools=0；${manualText}）`
                : `\`servers.${entry.serverId}\` -> toolset \`${entry.serverId}\` (status=disabled, tools=0; ${manualText})`);
            continue;
        }
        lines.push(language === 'zh'
            ? `\`servers.${entry.serverId}\` -> toolset \`${entry.serverId}\`（状态=声明无效：${firstNonEmptyLine(entry.errorText ?? '')}；${manualText}）`
            : `\`servers.${entry.serverId}\` -> toolset \`${entry.serverId}\` (status=invalid declaration: ${firstNonEmptyLine(entry.errorText ?? '')}; ${manualText})`);
    }
    const tail = language === 'zh'
        ? [
            '说明：`已声明但未加载` 通常表示当前会话尚未完成 MCP 重载，或 server 启动失败。',
            '说明：`已禁用` 表示该 server 在 `.minds/mcp.yaml` 中为 `enabled: false`，会保留 0 工具 toolset 和手册可见性。',
            '说明：MCP toolset 未配置 `manual` 不等于“不可用”，但会在 Problems 中给 warning，提醒团队补充整体定位说明。标准工具说明与参数来自 MCP `tools/list`，详见各工具函数说明；manual 至少应列工具名清单以澄清归属关系，再写可选团队增强说明。',
            '建议：运行 `team_mgmt_validate_mcp_cfg({})`，必要时执行 `mcp_restart`。',
        ]
        : [
            '`declared but not loaded` usually means MCP reload has not completed in the current session, or server startup failed.',
            '`disabled` means the server has `enabled: false` in `.minds/mcp.yaml`; Dominds keeps a zero-tool toolset and manual visible.',
            'Important: an unconfigured MCP `manual` does NOT mean the toolset is unavailable, but Dominds reports a Problems warning to encourage a short overall positioning note. Standard tool descriptions and arguments come from MCP `tools/list`; see each function-tool description. The manual should at least list tool names to clarify attribution, then add optional team enhancement guidance.',
            'Recommendation: run `team_mgmt_validate_mcp_cfg({})`, then `mcp_restart` if needed.',
        ];
    return header + fmtList(lines.concat(tail));
}
function renderMcpToolsetSetupGuideSection(language, snapshot) {
    const title = language === 'zh'
        ? fmtSubHeader('MCP toolset 设置方法与示例')
        : fmtSubHeader('How To Set Up MCP Toolsets (with example)');
    const fallbackServerId = 'your_mcp_server';
    const exampleServerId = snapshot.kind === 'loaded' && snapshot.entries.length > 0
        ? (snapshot.entries[0]?.serverId ?? fallbackServerId)
        : fallbackServerId;
    const exampleTransport = snapshot.kind === 'loaded'
        ? (snapshot.entries.find((e) => e.transport !== 'invalid')?.transport ?? 'stdio')
        : 'stdio';
    const notes = language === 'zh'
        ? fmtList([
            `步骤 1：在 \`${MCP_YAML_REL}\` 声明 \`servers.${exampleServerId}\`。`,
            `步骤 2：在 \`${TEAM_YAML_REL}\` 的 \`members.<id>.toolsets\` 添加 \`${exampleServerId}\`。`,
            '规则：MCP toolset 名称恒等于 `serverId`（不加前缀）。',
        ])
        : fmtList([
            `Step 1: declare \`servers.${exampleServerId}\` in \`${MCP_YAML_REL}\`.`,
            `Step 2: add \`${exampleServerId}\` under \`members.<id>.toolsets\` in \`${TEAM_YAML_REL}\`.`,
            'Rule: MCP toolset name is exactly the `serverId` (no prefix).',
        ]);
    const mcpExample = exampleTransport === 'streamable_http'
        ? fmtCodeBlock('yaml', [
            '# .minds/mcp.yaml',
            'version: 1',
            'servers:',
            `  ${exampleServerId}:`,
            '    truely-stateless: false',
            '    transport: streamable_http',
            '    url: http://127.0.0.1:3000/mcp',
            '    transform: []',
            '    tools: { whitelist: [], blacklist: [] }',
        ])
        : fmtCodeBlock('yaml', [
            '# .minds/mcp.yaml',
            'version: 1',
            'servers:',
            `  ${exampleServerId}:`,
            '    truely-stateless: false',
            '    transport: stdio',
            '    command: npx',
            "    args: ['-y', '@some/mcp-server@latest']",
            '    transform: []',
            '    tools: { whitelist: [], blacklist: [] }',
        ]);
    const teamExample = fmtCodeBlock('yaml', [
        '# .minds/team.yaml',
        'members:',
        '  operator:',
        '    toolsets:',
        '      - ws_read',
        `      - ${exampleServerId}`,
    ]);
    return title + notes + mcpExample + teamExample;
}
async function renderMcpManual(language) {
    const mcpSnapshot = await readMcpToolsetMappingSnapshot();
    const mcpMapping = renderMcpToolsetMappingSection(language, mcpSnapshot);
    const mcpSetup = renderMcpToolsetSetupGuideSection(language, mcpSnapshot);
    const mcpManualDetails = renderMcpToolsetManualDetailsSection(language, mcpSnapshot);
    if (language === 'zh') {
        return (fmtHeader('.minds/mcp.yaml') +
            fmtList([
                '每个 MCP `serverId` 注册一个 toolset，toolset 名称 = `serverId`（不加 `mcp_` 前缀）。成员通过 `members.<id>.toolsets` 选择能用哪些 MCP toolset。',
                '支持热重载：编辑 `.minds/mcp.yaml` 后通常无需重启 Dominds；必要时用 `mcp_restart`。`mcp_restart` 会把目标 server 的 `enabled: false` 写回 `enabled: true` 后尝试启动；成功后替换全局 MCP 工具注册，并清掉旧连接上的全部对话 lease。',
                '默认按“每个对话租用一个 MCP 连接/进程”运行（更安全）：某个对话首次使用该 server 时，可能为它启动/持有一个连接或进程（HTTP 连接或 stdio 进程），并添加 sticky reminder。确认当前对话不再需要该连接/进程后，用 `mcp_release` 释放。租约只表达连接/进程资源归属，不决定该 server 的全局工具注册/可见性；如确实是无状态服务器，可配置 `truely-stateless: true` 允许跨对话共享。若需停用某 server，用 `mcp_disable` 写入 `enabled: false`；禁用后仍会保留 0 工具 toolset 与 Raw MCP 基线/手册可见性。',
                'stdio 配置格式：`command` 必须是字符串（可执行命令），参数放在 `args`（string[]，可省略，默认空数组）。`cwd` 可选（字符串）：用于固定相对路径解析目录。',
                'HTTP headers 支持三种值：字面量字符串、`{ env: NAME }`、`{ prefix: "Bearer ", env: NAME }`；认证 token 建议从环境变量读取，不要写死在 YAML。',
                '用 `tools.whitelist/blacklist` 控制暴露的工具。顶层 `transform` 是 tools/prompts/resources/resource skills 的默认 ID 转换；内层 `tools.transform`、`prompts.transform`、`resources.transform`、`resources.skills.transform` 会覆盖外层转换，不会叠加；显式 `transform: []` 表示该层不转换。',
                '常见坑：stdio transport 需要可执行命令路径正确，且受成员权限（目录 + 扩展名：`*_dirs/no_*_dirs/*_file_ext_names/no_*_file_ext_names`）约束；HTTP transport 需要服务可达（url/端口/网络）。',
                '高频坑（stdio 路径）：若未设置 `cwd`，相对路径按 Dominds 进程工作目录（通常 rtws 根目录）解析；建议显式配置 `cwd` 或直接使用绝对路径。`cwd` 必须存在且是目录。',
                '可选手册字段：`servers.<serverId>.manual` 是增强说明，不是标准 MCP 接入的必填项。',
                '如果你要控制最终 `man({ "toolsetId": "<serverId>" })` 给 LLM 看的正式手册，请使用 `manual.contentFile` 指向 topic 文件目录前缀。',
                '如果你只是想在 `team_mgmt` 的 MCP 章节里补充团队管理说明，也可以使用 inline `content`（总说明）+ `sections`（章节列表）。这类 inline 内容会展示在 `team_mgmt` 指南里，但不会替代 `contentFile` 手册。',
                '重要：未配置 `manual` 并不代表 MCP toolset 不可用；但 Dominds 会在 Problems 中给 warning，提醒补充整体定位说明。标准工具元数据来自 MCP `tools/list`，智能体应继续依据各工具函数说明判断并使用。',
                '团队管理者建议：配置并验证 MCP 后，优先依赖 MCP server 暴露的工具 description/参数；同时给每个 MCP server 的手册写一段简短整体定位，并列出工具名清单，明确“哪些工具属于这个 toolset”。只有当团队需要补充更完整的使用场景、安全边界、故障处置或协作规范时，再写 `manual.contentFile` 或更详细的 inline `content + sections`。',
                '工具列表写法：列工具名即可，必要时补一句用途归类；不要复制参数 schema 或长篇工具 description，因为这些契约由系统提示里的函数工具定义提供。',
                '可选增强的章节组织建议采用“半结构化”：可优先考虑 `何时使用`、`安全边界`、`不可用时业务处置` 这类高价值章节，但不要求所有 toolset 都照抄同一模板。',
                '对高风险 MCP toolset，建议明确“不可用时业务处置规约”：至少回答 1) 当前 toolset 暂不可达时是否必须找协调者/专员接手；2) 是否允许走人工或其他工具链降级路径；3) 哪些业务动作在该 toolset 恢复前必须暂停，禁止擅自继续。',
                '最小诊断流程（建议顺序）：1) 先用 `team_mgmt_check_provider({ provider_key: "<providerKey>", model: "", all_models: false, live: false })` 确认 LLM provider 可用；2) 再检查该成员的目录权限（`man({ "toolsetId": "team_mgmt", "topics": ["permissions"] })`）；3) 运行 `team_mgmt_validate_mcp_cfg({})` 汇总 `.minds/mcp.yaml` 与 MCP 问题；4) 必要时 `mcp_restart` 启用/重启，或 `mcp_disable` 禁用；只在当前对话确实持有不再需要的 lease 时使用 `mcp_release`。',
            ]) +
            fmtCodeBlock('yaml', [
                '# 最小模板（stdio）',
                'version: 1',
                'servers:',
                '  sdk_stdio:',
                '    truely-stateless: false',
                '    transport: stdio',
                '    command: npx',
                "    args: ['-y', '@some/mcp-server@latest']",
                '    cwd: "."',
                '    env: {}',
                '    transform: []',
                '    tools: { whitelist: [], blacklist: [] }',
            ]) +
            fmtCodeBlock('yaml', [
                '# stdio 路径示例（最小）',
                '# 相对路径：配合 cwd 固定解析目录',
                'command: node',
                "args: ['./mcp/server.js']",
                'cwd: "/absolute/path/to/project"',
                '',
                '# 绝对路径：更稳，不依赖 cwd',
                'command: node',
                "args: ['/absolute/path/to/mcp/server.js']",
            ]) +
            fmtCodeBlock('yaml', [
                '# 最小模板（HTTP）',
                'version: 1',
                'servers:',
                '  sdk_http:',
                '    truely-stateless: false',
                '    transport: streamable_http',
                '    url: http://127.0.0.1:3000/mcp',
                '    # headers:',
                '    #   Authorization:',
                '    #     prefix: "Bearer "',
                '    #     env: MCP_AUTH_TOKEN',
                '    transform: []',
                '    tools: { whitelist: [], blacklist: [] }',
            ]) +
            fmtCodeBlock('yaml', [
                '# 可选 manual 增强示例（放在 servers.<serverId> 下；不是 MCP 接入必填项）',
                'manual:',
                '  content: |',
                '    这个 MCP toolset 负责 xx，优先用于 xx 场景。工具归属：foo_lookup、foo_export。',
                '  sections:',
                "    - title: '工具列表'",
                '      content: |',
                '        - foo_lookup：查询 xx。',
                '        - foo_export：导出 xx。',
                "    - title: '何时使用'",
                '      content: |',
                '        1) 当你需要 ...',
                '        2) 执行前先确认 ...',
                "    - title: '安全边界'",
                '      content: |',
                '        - 不要用于 ...',
                '        - 若失败先看 ...',
                "    - title: '不可用时业务处置'",
                '      content: |',
                '        - 若该 toolset 暂时不可达，先找 @coordinator（或指定协调者）确认是否改走降级路径。',
                '        - 仅允许改用 ... 作为临时替代；若涉及 ...，必须等待该 MCP 恢复，不可继续。',
            ]) +
            mcpMapping +
            mcpSetup +
            mcpManualDetails);
    }
    return (fmtHeader('.minds/mcp.yaml') +
        fmtList([
            'Each MCP `serverId` registers one toolset, and the toolset name is exactly `serverId` (no `mcp_` prefix). Members choose MCP access via `members.<id>.toolsets`.',
            'Hot reload: edits usually apply without restarting Dominds; use `mcp_restart` when needed. `mcp_restart` writes `enabled: true` when the target server is currently `enabled: false`, then tries to start it; after success it replaces the global MCP tool registration and clears all dialog leases on the old connection/process.',
            "Default is per-dialog MCP connection/process leasing (safer): a dialog's first use may start/hold one connection or process for that server (an HTTP connection or stdio process), and first use adds a sticky reminder. Call `mcp_release` when you're sure the current dialog no longer needs that connection/process. This lease is about connection/process resource ownership only; tool registration/visibility still follows the latest global server connection. If the server is truly stateless, set `truely-stateless: true` to allow cross-dialog sharing. To disable a server, use `mcp_disable` to write `enabled: false`; disabled servers remain visible as zero-tool toolsets with Raw MCP baseline/manual visibility.",
            'Stdio shape: `command` must be a string executable; parameters go in `args` (string[], optional, defaults to empty). Optional `cwd` (string) fixes the working directory used for relative paths.',
            'HTTP headers support three value forms: literal strings, `{ env: NAME }`, and `{ prefix: "Bearer ", env: NAME }`; keep auth tokens in env instead of hardcoding them in YAML.',
            'Use `tools.whitelist/blacklist` for exposure control. Top-level `transform` is the default ID transform for tools/prompts/resources/resource skills; nested `tools.transform`, `prompts.transform`, `resources.transform`, and `resources.skills.transform` override the enclosing transform instead of stacking with it. Explicit `transform: []` means no transform for that layer.',
            'Common pitfalls: stdio transport needs a correct executable/command path, and is subject to member permissions (directory + extension: `*_dirs/no_*_dirs/*_file_ext_names/no_*_file_ext_names`); HTTP transport requires the server URL to be reachable.',
            'High-frequency pitfall (stdio paths): if `cwd` is omitted, relative paths are resolved from Dominds process cwd (usually rtws root). Prefer setting `cwd` explicitly or use absolute paths. `cwd` must exist and be a directory.',
            'Optional manual field: `servers.<serverId>.manual` is enhancement guidance, not required for standard MCP use.',
            'To control the final formal manual shown to the LLM by `man({ "toolsetId": "<serverId>" })`, use `manual.contentFile` and point it at the topic-file directory prefix.',
            'If you only want extra team-management guidance inside the `team_mgmt` MCP chapter, you may also use inline `content` (overview) + `sections` (chapter list). That inline content is shown in the `team_mgmt` guide, but it does not replace `contentFile` manuals.',
            'Important: an unconfigured `manual` does not mean the MCP toolset is unavailable, but Dominds reports a Problems warning to encourage an overall positioning note. Standard tool metadata comes from MCP `tools/list`; continue by reading each function-tool description.',
            'Team-manager recommendation: after MCP config is validated, rely on MCP-provided descriptions/arguments first; also write a short overall positioning note for each MCP server manual and list tool names to make clear which tools belong to this toolset. Add `manual.contentFile` or more detailed inline `content + sections` only when the team needs fuller guidance about use cases, safety boundaries, failure handling, or coordination norms.',
            'Tool-list writing rule: list tool names, with a short purpose group when useful; do not copy parameter schemas or long tool descriptions, because those contracts are provided by function-tool definitions in the system prompt.',
            'Use a semi-structured chapter shape for optional enhancements: high-value sections often include `When To Use`, `Guardrails`, and `Business Handling When Unavailable`, but do not force every toolset into one fixed template.',
            'For high-risk MCP toolsets, document unavailable-case business rules explicitly: at minimum answer 1) whether a temporarily unavailable toolset must be escalated to a coordinator or specialist, 2) whether a human-operated or alternate-tool fallback path is allowed, and 3) which business actions must pause until this toolset recovers.',
            'Minimal diagnostic flow: 1) run `team_mgmt_check_provider({ provider_key: "<providerKey>", model: "", all_models: false, live: false })` to confirm the LLM provider works; 2) review member directory permissions (`man({ "toolsetId": "team_mgmt", "topics": ["permissions"] })`); 3) run `team_mgmt_validate_mcp_cfg({})` to summarize `.minds/mcp.yaml` + MCP issues; 4) use `mcp_restart` to enable/restart, or `mcp_disable` to disable; use `mcp_release` only when the current dialog actually holds a lease it no longer needs.',
        ]) +
        fmtCodeBlock('yaml', [
            '# Minimal template (stdio)',
            'version: 1',
            'servers:',
            '  sdk_stdio:',
            '    truely-stateless: false',
            '    transport: stdio',
            '    command: npx',
            "    args: ['-y', '@some/mcp-server@latest']",
            '    cwd: "."',
            '    env: {}',
            '    transform: []',
            '    tools: { whitelist: [], blacklist: [] }',
        ]) +
        fmtCodeBlock('yaml', [
            '# stdio path example (minimal)',
            '# Relative path: stable with explicit cwd',
            'command: node',
            "args: ['./mcp/server.js']",
            'cwd: "/absolute/path/to/project"',
            '',
            '# Absolute path: more stable, independent of cwd',
            'command: node',
            "args: ['/absolute/path/to/mcp/server.js']",
        ]) +
        fmtCodeBlock('yaml', [
            '# Minimal template (HTTP)',
            'version: 1',
            'servers:',
            '  sdk_http:',
            '    truely-stateless: false',
            '    transport: streamable_http',
            '    url: http://127.0.0.1:3000/mcp',
            '    # headers:',
            '    #   Authorization:',
            '    #     prefix: "Bearer "',
            '    #     env: MCP_AUTH_TOKEN',
            '    transform: []',
            '    tools: { whitelist: [], blacklist: [] }',
        ]) +
        fmtCodeBlock('yaml', [
            '# Optional manual enhancement example (place under servers.<serverId>; not required for MCP integration)',
            'manual:',
            '  content: |',
            '    This MCP toolset is for xx and should be preferred in xx cases. Tool attribution: foo_lookup, foo_export.',
            '  sections:',
            "    - title: 'Tools'",
            '      content: |',
            '        - foo_lookup: query xx.',
            '        - foo_export: export xx.',
            "    - title: 'When To Use'",
            '      content: |',
            '        1) Use when ...',
            '        2) Confirm ... before execution.',
            "    - title: 'Guardrails'",
            '      content: |',
            '        - Avoid using for ...',
            '        - On failures, first inspect ...',
            "    - title: 'Business Handling When Unavailable'",
            '      content: |',
            '        - If this toolset is temporarily unavailable, ask @coordinator (or the designated coordinator) whether to switch to the fallback path.',
            '        - Only use ... as an interim fallback; if the task touches ..., wait for this MCP to recover instead of proceeding.',
        ]) +
        mcpMapping +
        mcpSetup +
        mcpManualDetails);
}
async function measureRenderedTeamMgmtMcpTopicRawChars() {
    const zh = await renderMcpManual('zh');
    const en = await renderMcpManual('en');
    return Math.max(zh.length, en.length);
}
