// tools/create-spec/mcp-spec-adapter.ts
// SPEC-016b: Generates specialized spec content for MCP server projects.
// Criteria 1, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16.
import { isMcpServerProject } from '../../engine/detectors/mcp-server-detector.js';
// === Detection helpers ===
export function isMcpSpec(knowledge) {
    if (!knowledge) {
        return false;
    }
    return isMcpServerProject(knowledge);
}
// === Default tool definitions for different MCP server types ===
function buildDefaultMcpTools(description) {
    const lower = description.toLowerCase();
    const tools = [];
    if (lower.includes('file') || lower.includes('read') || lower.includes('write')) {
        tools.push({
            name: 'read_file',
            description: 'Read the contents of a file from the filesystem',
            inputSchema: {
                type: 'object',
                properties: {
                    path: { type: 'string', description: 'Absolute path to the file' },
                },
                required: ['path'],
                additionalProperties: false,
            },
            outputFormat: 'text',
            errorCases: ['File not found', 'Permission denied', 'Path is a directory'],
        });
    }
    if (lower.includes('search') || lower.includes('query') || lower.includes('find')) {
        tools.push({
            name: 'search',
            description: 'Search for content matching a query pattern',
            inputSchema: {
                type: 'object',
                properties: {
                    query: { type: 'string', description: 'Search query or pattern' },
                    limit: { type: 'number', description: 'Maximum number of results (default: 10)' },
                },
                required: ['query'],
                additionalProperties: false,
            },
            outputFormat: 'text',
            errorCases: ['Invalid query syntax', 'Search timeout', 'Index unavailable'],
        });
    }
    if (tools.length === 0) {
        tools.push({
            name: 'execute',
            description: 'Execute the primary operation of this MCP server',
            inputSchema: {
                type: 'object',
                properties: {
                    input: { type: 'string', description: 'The input data for the operation' },
                },
                required: ['input'],
                additionalProperties: false,
            },
            outputFormat: 'text',
            errorCases: ['Invalid input', 'Operation failed', 'Timeout exceeded'],
        });
    }
    return tools;
}
function buildDefaultMcpResources(description) {
    const lower = description.toLowerCase();
    const resources = [];
    if (lower.includes('config') || lower.includes('settings')) {
        resources.push({
            uri: 'config://server/settings',
            mimeType: 'application/json',
            description: 'Server configuration and settings',
        });
    }
    if (lower.includes('schema') || lower.includes('type') || lower.includes('definition')) {
        resources.push({
            uri: 'schema://types',
            mimeType: 'application/json',
            description: 'Type definitions and JSON schemas',
        });
    }
    return resources;
}
function detectTransportFromDescription(description) {
    const lower = description.toLowerCase();
    if (lower.includes('http') || lower.includes('rest') || lower.includes('web')) {
        return 'http-streamable';
    }
    if (lower.includes('sse') || lower.includes('server-sent')) {
        return 'sse';
    }
    return 'stdio';
}
// === MCP server spec generator ===
export function generateMcpServerSpec(_title, description, _knowledge) {
    const tools = buildDefaultMcpTools(description);
    const resources = buildDefaultMcpResources(description);
    const transport = detectTransportFromDescription(description);
    return {
        tools,
        resources,
        prompts: [],
        transport,
        protocolVersion: '2024-11-05',
        compliance: [
            'All tools have non-empty descriptions',
            'All inputSchemas use type: "object" with required and additionalProperties: false',
            'Errors return McpError with correct code (InvalidParams, InternalError, MethodNotFound)',
            'No tool call blocks the event loop without a timeout',
        ],
    };
}
// === HU.md section builder for MCP specs ===
export function buildMcpHuSection(title, description, knowledge) {
    const spec = generateMcpServerSpec(title, description, knowledge);
    const lines = ['', '## MCP Server Specification', ''];
    lines.push('### Tools');
    for (const tool of spec.tools) {
        lines.push(`#### \`${tool.name}\``);
        lines.push(`- **Description**: ${tool.description}`);
        lines.push(`- **Output format**: ${tool.outputFormat}`);
        lines.push('- **Input schema**:');
        lines.push('  ```json');
        lines.push(`  ${JSON.stringify(tool.inputSchema, null, 2).split('\n').join('\n  ')}`);
        lines.push('  ```');
        if (tool.errorCases.length > 0) {
            lines.push(`- **Error cases**: ${tool.errorCases.join(', ')}`);
        }
        lines.push('');
    }
    if (spec.resources.length > 0) {
        lines.push('### Resources');
        for (const resource of spec.resources) {
            lines.push(`- **${resource.uri}** (${resource.mimeType}): ${resource.description}`);
        }
        lines.push('');
    }
    lines.push('### Transport');
    lines.push(`Protocol: \`${spec.transport}\` | MCP Protocol version: \`${spec.protocolVersion}\``);
    lines.push('');
    lines.push('### Acceptance Criteria (MCP Compliance)');
    for (const rule of spec.compliance) {
        lines.push(`- [ ] ${rule}`);
    }
    lines.push('');
    lines.push('### Acceptance Criteria (Tool Registration)');
    for (const tool of spec.tools) {
        lines.push(`- [ ] Tool \`${tool.name}\` is registered and appears in tools/list`);
        lines.push(`- [ ] \`${tool.name}\` accepts valid input and returns expected output`);
        lines.push(`- [ ] \`${tool.name}\` returns InvalidParams on invalid input`);
    }
    lines.push('');
    lines.push('### Acceptance Criteria (Protocol)');
    lines.push('- [ ] Server responds correctly to `initialize` handshake');
    lines.push('- [ ] Server handles concurrent tool calls without thread-safety issues');
    lines.push('- [ ] All tools respect 30s timeout (MCP client default)');
    lines.push('- [ ] Extra fields in tool input are rejected when `additionalProperties: false` is set');
    return lines.join('\n');
}
// === Observability spec generator (criterion 16) ===
export function generateObservabilitySpec(description) {
    const lower = description.toLowerCase();
    const hasTokenBudget = lower.includes('budget') || lower.includes('cost') || lower.includes('token');
    const tracing = [
        'Trace every tool call: input args, output content, duration (ms), error if any',
        'Propagate trace context across tool call chains (W3C TraceContext or OpenTelemetry)',
        'Label each span with: tool_name, server_name, protocol_version',
    ];
    const budgetAlerts = [
        'Alert when session token usage exceeds 80% of configured budget',
        'Alert when a single tool call takes > 10s (warn) or > 30s (error)',
    ];
    if (hasTokenBudget) {
        budgetAlerts.push('Alert when monthly budget threshold is reached (configurable via env var)');
    }
    const structuredLogs = [
        'Log every tool call as structured JSON: {timestamp, tool, input_hash, duration_ms, status}',
        'Log server startup with: transport, tools_count, resources_count, protocol_version',
        'Log all errors with: tool_name, error_code, error_message, stack_trace',
    ];
    const metricsSchema = [
        'tool_call_count: counter by tool_name and status (success/error)',
        'tool_call_duration_ms: histogram by tool_name (p50, p95, p99)',
        'tool_error_rate: gauge = errors / total calls, by tool_name',
        'most_called_tool: ranking of tools by invocation count',
    ];
    return { tracing, budgetAlerts, structuredLogs, metricsSchema };
}
// === Breaking change detection helpers (criterion 19) ===
export function detectMcpBreakingChange(toolName, oldSchema, newSchema) {
    const oldProps = oldSchema.properties ?? {};
    const newProps = newSchema.properties ?? {};
    const oldRequired = oldSchema.required ?? [];
    const newRequired = newSchema.required ?? [];
    // Check for removed required parameters
    for (const field of oldRequired) {
        if (!newRequired.includes(field)) {
            return {
                toolName,
                changeType: 'param-removed',
                affectedField: field,
                migrationCriteria: `Clients must stop passing \`${field}\` or use a compatibility wrapper. Update all integrations before deploying.`,
            };
        }
    }
    // Check for removed properties
    for (const field of Object.keys(oldProps)) {
        if (!(field in newProps)) {
            return {
                toolName,
                changeType: 'param-removed',
                affectedField: field,
                migrationCriteria: `Remove all uses of \`${field}\` in client code. This field is no longer accepted.`,
            };
        }
    }
    // Check for type changes
    for (const field of Object.keys(oldProps)) {
        const oldType = oldProps[field]?.type;
        const newType = newProps[field]?.type;
        if (oldType !== undefined &&
            newType !== undefined &&
            oldType !== newType &&
            typeof oldType === 'string' &&
            typeof newType === 'string') {
            return {
                toolName,
                changeType: 'param-type-changed',
                affectedField: field,
                migrationCriteria: `Update \`${field}\` from type \`${oldType}\` to \`${newType}\` in all client calls.`,
            };
        }
    }
    return null;
}
//# sourceMappingURL=mcp-spec-adapter.js.map