#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { log } from "./src/utils/index.js";
import { ALLOW_DELETE_OPERATION, ALLOW_DDL_OPERATION, ALLOW_INSERT_OPERATION, ALLOW_UPDATE_OPERATION, SCHEMA_DELETE_PERMISSIONS, SCHEMA_DDL_PERMISSIONS, SCHEMA_INSERT_PERMISSIONS, SCHEMA_UPDATE_PERMISSIONS, isMultiDbMode, mcpConfig as config, MCP_VERSION as version, IS_REMOTE_MCP, REMOTE_SECRET_KEY, PORT, } from "./src/config/index.js";
import { safeExit, executeReadOnlyQuery, testConnection, getSchema, } from "./src/db/index.js";
import path from 'path';
import express from "express";
import { fileURLToPath } from 'url';
log("info", `Starting MySQL MCP server v${version}...`);
const toolVersion = `MySQL MCP Server [v${process.env.npm_package_version}]`;
let toolDescription = `[${toolVersion}] Run SQL queries against MySQL database`;
if (isMultiDbMode) {
    toolDescription += " (Multi-DB mode enabled)";
}
if (ALLOW_INSERT_OPERATION ||
    ALLOW_UPDATE_OPERATION ||
    ALLOW_DELETE_OPERATION ||
    ALLOW_DDL_OPERATION) {
    toolDescription += " with support for:";
    if (ALLOW_INSERT_OPERATION) {
        toolDescription += " INSERT,";
    }
    if (ALLOW_UPDATE_OPERATION) {
        toolDescription += " UPDATE,";
    }
    if (ALLOW_DELETE_OPERATION) {
        toolDescription += " DELETE,";
    }
    if (ALLOW_DDL_OPERATION) {
        toolDescription += " DDL,";
    }
    toolDescription = toolDescription.replace(/,$/, "") + " and READ operations";
    if (Object.keys(SCHEMA_INSERT_PERMISSIONS).length > 0 ||
        Object.keys(SCHEMA_UPDATE_PERMISSIONS).length > 0 ||
        Object.keys(SCHEMA_DELETE_PERMISSIONS).length > 0 ||
        Object.keys(SCHEMA_DDL_PERMISSIONS).length > 0) {
        toolDescription += " (Schema-specific permissions enabled)";
    }
}
else {
    toolDescription += " (READ-ONLY)";
}
log("info", "WordPress API Configuration:", JSON.stringify({
    apiUrl: config.wordpress.apiUrl,
    apiKey: config.wordpress.apiKey ? "******" : "not set",
    connectionType: "REST API",
    readOnly: true,
}, null, 2));
export const configSchema = z.object({
    debug: z.boolean().default(false).describe("Enable debug logging"),
});
export default function createMcpServer({ sessionId, config, }) {
    const server = new Server({
        name: "MySQL MCP Server",
        version: process.env.npm_package_version || "1.0.0",
    }, {
        capabilities: {
            resources: {},
            tools: {
                mysql_query: {
                    description: toolDescription,
                    inputSchema: {
                        type: "object",
                        properties: {
                            sql: {
                                type: "string",
                                description: "The SQL query to execute",
                            },
                        },
                        required: ["sql"],
                    },
                },
            },
        },
    });
    server.setRequestHandler(ListResourcesRequestSchema, async () => {
        try {
            log("info", "Handling ListResourcesRequest");
            log("info", `Using WordPress API`);
            const schemaResult = await getSchema();
            const tables = schemaResult.tables || [];
            log("info", `Found ${tables.length} tables via WordPress API`);
            const resources = tables.map((tableName) => ({
                uri: `mysql://tables/${tableName}`,
                name: tableName,
                title: tableName,
                description: `WordPress database table: ${tableName}`,
                mimeType: "application/json",
            }));
            resources.push({
                uri: "mysql://tables",
                name: "Tables",
                title: "MySQL Tables",
                description: "List of all MySQL tables",
                mimeType: "application/json",
            });
            return { resources };
        }
        catch (error) {
            log("error", "Error in ListResourcesRequest handler:", error);
            throw error;
        }
    });
    server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
        try {
            log("info", "Handling ReadResourceRequest:", request.params.uri);
            const uriParts = request.params.uri.split("/");
            const tableName = uriParts.pop();
            if (!tableName) {
                throw new Error(`Invalid resource URI: ${request.params.uri}`);
            }
            const schemaResult = await getSchema(tableName);
            const columns = schemaResult.columns || [];
            return {
                contents: [
                    {
                        uri: request.params.uri,
                        mimeType: "application/json",
                        text: JSON.stringify(columns, null, 2),
                    },
                ],
            };
        }
        catch (error) {
            log("error", "Error in ReadResourceRequest handler:", error);
            throw error;
        }
    });
    server.setRequestHandler(CallToolRequestSchema, async (request) => {
        try {
            log("info", "Handling CallToolRequest:", request.params.name);
            if (request.params.name !== "mysql_query") {
                throw new Error(`Unknown tool: ${request.params.name}`);
            }
            const sql = request.params.arguments?.sql;
            return await executeReadOnlyQuery(sql);
        }
        catch (err) {
            const error = err;
            log("error", "Error in CallToolRequest handler:", error);
            return {
                content: [{
                        type: "text",
                        text: `Error: ${error.message}`
                    }],
                isError: true
            };
        }
    });
    server.setRequestHandler(ListToolsRequestSchema, async () => {
        log("info", "Handling ListToolsRequest");
        const toolsResponse = {
            tools: [
                {
                    name: "mysql_query",
                    description: toolDescription,
                    inputSchema: {
                        type: "object",
                        properties: {
                            sql: {
                                type: "string",
                                description: "The SQL query to execute",
                            },
                        },
                        required: ["sql"],
                    },
                },
            ],
        };
        log("info", "ListToolsRequest response:", JSON.stringify(toolsResponse, null, 2));
        return toolsResponse;
    });
    (async () => {
        try {
            log("info", "Attempting to test WordPress API connection...");
            const isConnected = await testConnection();
            if (isConnected) {
                log("info", "WordPress API connection test successful");
            }
            else {
                log("error", "WordPress API connection test failed");
                safeExit(1);
            }
        }
        catch (error) {
            log("error", "Fatal error during server startup:", error);
            safeExit(1);
        }
    })();
    const shutdown = async (signal) => {
        log("error", `Received ${signal}. Shutting down...`);
        log("info", "Shutdown complete");
    };
    process.on("SIGINT", async () => {
        try {
            await shutdown("SIGINT");
            process.exit(0);
        }
        catch (err) {
            log("error", "Error during SIGINT shutdown:", err);
            safeExit(1);
        }
    });
    process.on("SIGTERM", async () => {
        try {
            await shutdown("SIGTERM");
            process.exit(0);
        }
        catch (err) {
            log("error", "Error during SIGTERM shutdown:", err);
            safeExit(1);
        }
    });
    process.on("uncaughtException", (error) => {
        log("error", "Uncaught exception:", error);
        safeExit(1);
    });
    process.on("unhandledRejection", (reason, promise) => {
        log("error", "Unhandled rejection at:", promise, "reason:", reason);
        safeExit(1);
    });
    return server;
}
const isMainModule = () => {
    if (typeof require !== 'undefined' && require.main === module) {
        return true;
    }
    if (typeof import.meta !== 'undefined' && import.meta.url && process.argv[1]) {
        const currentModulePath = fileURLToPath(import.meta.url);
        const mainScriptPath = path.resolve(process.argv[1]);
        return currentModulePath === mainScriptPath;
    }
    return false;
};
if (isMainModule()) {
    log("info", "Running in standalone mode");
    (async () => {
        try {
            const mcpServer = createMcpServer({ config: { debug: false } });
            if (IS_REMOTE_MCP && REMOTE_SECRET_KEY?.length) {
                const app = express();
                app.use(express.json());
                app.post("/mcp", async (req, res) => {
                    if (!req.get("Authorization") ||
                        !req.get("Authorization")?.startsWith("Bearer ") ||
                        !req.get("Authorization")?.endsWith(REMOTE_SECRET_KEY)) {
                        console.error("Missing or invalid Authorization header");
                        res.status(401).json({
                            jsonrpc: "2.0",
                            error: {
                                code: -32603,
                                message: "Missing or invalid Authorization header",
                            },
                            id: null,
                        });
                        return;
                    }
                    try {
                        const server = mcpServer;
                        const transport = new StreamableHTTPServerTransport({
                            sessionIdGenerator: undefined,
                        });
                        res.on("close", () => {
                            log("info", "Request closed");
                            transport.close();
                            server.close();
                        });
                        await server.connect(transport);
                        await transport.handleRequest(req, res, req.body);
                    }
                    catch (error) {
                        log("error", "Error handling MCP request:", error);
                        if (!res.headersSent) {
                            res.status(500).json({
                                jsonrpc: "2.0",
                                error: {
                                    code: -32603,
                                    message: error.message,
                                },
                                id: null,
                            });
                        }
                    }
                });
                app.get("/mcp", async (req, res) => {
                    console.log("Received GET MCP request");
                    res.writeHead(405).end(JSON.stringify({
                        jsonrpc: "2.0",
                        error: {
                            code: -32000,
                            message: "Method not allowed.",
                        },
                        id: null,
                    }));
                });
                app.delete("/mcp", async (req, res) => {
                    console.log("Received DELETE MCP request");
                    res.writeHead(405).end(JSON.stringify({
                        jsonrpc: "2.0",
                        error: {
                            code: -32000,
                            message: "Method not allowed.",
                        },
                        id: null,
                    }));
                });
                app.listen(PORT, (error) => {
                    if (error) {
                        console.error("Failed to start server:", error);
                        process.exit(1);
                    }
                    console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);
                });
            }
            else {
                const transport = new StdioServerTransport();
                await mcpServer.connect(transport);
                log("info", "Server started and listening on stdio");
            }
        }
        catch (error) {
            log("error", "Server error:", error);
            safeExit(1);
        }
    })();
}
