/*
 * Gearcoleco - ColecoVision Emulator
 * Copyright (C) 2021  Ignacio Sanchez

 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/
 *
 */

#include "mcp_server.h"
#include "../utils.h"
#include "../emu.h"
#include <sstream>
#include <iomanip>
#include <fstream>
#include <limits>
#include <cstdlib>
#include "log.h"

bool g_mcp_router_disabled = false;

template<typename T>
static bool parse_mcp_hex_with_prefix(const std::string& hex_str, T* result)
{
    const char* str = hex_str.c_str();
    size_t len = hex_str.length();

    if (len >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
    {
        str += 2;
        len -= 2;
    }
    else if (len >= 1 && str[0] == '$')
    {
        str += 1;
        len -= 1;
    }

    if (len == 0)
        return false;

    char* end = NULL;
    unsigned long value = strtoul(str, &end, 16);

    if (!end || *end != '\0')
        return false;
    if (value > (unsigned long)std::numeric_limits<T>::max())
        return false;

    *result = (T)value;
    return true;
}

static void* ReaderThreadFunc(void* arg)
{
    McpServer* server = (McpServer*)arg;
    server->ReaderLoop();
    return NULL;
}

static bool file_exists(const std::string& path)
{
    std::ifstream file(path.c_str());
    return file.good();
}

void McpServer::ReaderLoop()
{
    while (m_running.load())
    {
        std::string line;
        if (m_transport->recv(line))
        {
            if (!line.empty())
            {
                HandleLine(line);
            }
        }
        else
        {
            // EOF or error
            Stop();
            break;
        }
    }
}

void McpServer::Run()
{
    // Start reader thread for transport input
    std::thread reader_thread(ReaderThreadFunc, this);
    reader_thread.detach();

    // Transport thread waits for responses generated by the main thread
    // (via PumpCommands) and sends them back through the transport
    while (m_running.load())
    {
        DebugResponse* resp = m_responseQueue.WaitAndPop();
        if (resp == NULL)
            break;

        if (resp->isError)
        {
            SendError(resp->requestId, resp->errorCode, resp->errorMessage);
        }
        else
        {
            // Wrap result in MCP response format
            json mcpResult;
            mcpResult["content"] = json::array();

            // Check if this is image data (special marker from GetScreenshot)
            if (resp->result.contains("__mcp_image") && resp->result["__mcp_image"] == true)
            {
                // Image content type
                mcpResult["content"].push_back({
                    {"type", "image"},
                    {"data", resp->result["data"]},
                    {"mimeType", resp->result["mimeType"]}
                });
            }
            else
            {
                // Text content type (default)
                std::ostringstream result_ss;
                result_ss << resp->result.dump(2, ' ', false, json::error_handler_t::replace);

                mcpResult["content"].push_back({
                    {"type", "text"},
                    {"text", result_ss.str()}
                });
            }

            json response;
            response["jsonrpc"] = "2.0";
            response["id"] = resp->requestId;
            response["result"] = mcpResult;

            SendResponse(response);
        }

        SafeDelete(resp);
    }
}

void McpServer::HandleLine(const std::string& line)
{
    json request;

    // Try to parse JSON
    if (!json::accept(line))
    {
        SendError(0, -32700, "Parse error: Invalid JSON");
        return;
    }

    request = json::parse(line);

    // Validate JSON-RPC structure
    if (!request.contains("jsonrpc") || request["jsonrpc"] != "2.0")
    {
        SendError(0, -32600, "Invalid Request: missing or invalid jsonrpc version");
        return;
    }

    if (!request.contains("method") || !request["method"].is_string())
    {
        SendError(0, -32600, "Invalid Request: missing method");
        return;
    }

    std::string method = request["method"];

    // Handle different methods
    if (method == "initialize")
    {
        HandleInitialize(request);
    }
    else if (method == "notifications/initialized")
    {
        // Notifications don't need JSON-RPC response, but HTTP needs HTTP response
        if (dynamic_cast<HttpTransport*>(m_transport))
        {
            m_transport->send("{}");
        }
    }
    else if (method == "tools/list")
    {
        HandleToolsList(request);
    }
    else if (method == "tools/call")
    {
        HandleToolsCall(request);
    }
    else if (method == "resources/list")
    {
        HandleResourcesList(request);
    }
    else if (method == "resources/read")
    {
        HandleResourcesRead(request);
    }
    else
    {
        int64_t id = request.contains("id") ? request["id"].get<int64_t>() : 0;
        SendError(id, -32601, "Method not found: " + method);
    }
}

void McpServer::HandleInitialize(const json& request)
{
    if (!request.contains("id"))
    {
        SendError(0, -32600, "Invalid Request: missing id");
        return;
    }

    int64_t id = request["id"];

    // Get protocol version from request
    std::string protocolVersion = "2025-11-25";
    if (request.contains("params") && request["params"].contains("protocolVersion"))
    {
        protocolVersion = request["params"]["protocolVersion"];
    }

    json response;
    response["jsonrpc"] = "2.0";
    response["id"] = id;
    response["result"] = {
        {"protocolVersion", protocolVersion},
        {"capabilities", {
            {"tools", json::object()},
            {"resources", json::object()}
        }},
        {"serverInfo", {
            {"name", "gearcoleco-mcp-server"},
            {"title", "Gearcoleco MCP Server"},
            {"description", "Debug/control Gearcoleco ColecoVision: execution, breakpoints, memory, Z80 CPU, TMS9918 VDP, SN76489 PSG, AY-3-8910 SGM, disassembly, symbols, sprites, save states, rewind, keypad/input, screenshots."},
            {"version", GEARCOLECO_VERSION}
        }}
    };

    m_initialized = true;
    SendResponse(response);
}

json McpServer::BuildToolList()
{
    json tools = json::array();

    // Execution control tools
    tools.push_back({
        {"name", "debug_pause"},
        {"title", "Debug Pause"},
        {"description", "Pause execution at current instruction; enter debugger."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "debug_continue"},
        {"title", "Debug Continue"},
        {"description", "Resume emulator execution from pause or breakpoint."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "debug_step_into"},
        {"title", "Debug Step Into"},
        {"description", "Step next Z80 CPU instruction; enter CALL subroutines."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "debug_step_over"},
        {"title", "Debug Step Over"},
        {"description", "Step next Z80 CPU instruction; skip CALL subroutines."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "debug_step_out"},
        {"title", "Debug Step Out"},
        {"description", "Run until RET returns from current subroutine."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "debug_step_frame"},
        {"title", "Debug Step Frame"},
        {"description", "Run one or more video frames to VBlank."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"frames", {
                    {"type", "integer"},
                    {"description", "Number of frames to step. Default 1."},
                    {"minimum", 1},
                    {"maximum", 1000}
                }}
            }},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "debug_reset"},
        {"title", "Debug Reset"},
        {"description", "Reset the emulated ColecoVision system."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "debug_get_status"},
        {"title", "Debug Get Status"},
        {"description", "Read debugger state: paused, breakpoint hit, current PC."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    // Breakpoint tools
    tools.push_back({
        {"name", "set_breakpoint"},
        {"title", "Set Breakpoint"},
        {"description", "Add execute/read/write breakpoint at logical ROM/RAM, VRAM, or VDP register address."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"address", {
                    {"type", "string"},
                    {"description", "Logical hex address; ranges: rom_ram 0000-FFFF, vram 0000-3FFF, vdp_reg 00-07."}
                }},
                {"memory_area", {
                    {"type", "string"},
                    {"description", "Memory area: rom_ram default, vram, vdp_reg."},
                    {"enum", json::array({"rom_ram", "vram", "vdp_reg"})}
                }},
                {"read", {
                    {"type", "boolean"},
                    {"description", "Read access breakpoint; PC stops after the access. Default false."}
                }},
                {"write", {
                    {"type", "boolean"},
                    {"description", "Write access breakpoint; PC stops after the access. Default false."}
                }},
                {"execute", {
                    {"type", "boolean"},
                    {"description", "Execution breakpoint; only valid for rom_ram. Default true."}
                }}
            }},
            {"required", json::array({"address"})}
        }}
    });

    tools.push_back({
        {"name", "set_breakpoint_range"},
        {"title", "Set Breakpoint Range"},
        {"description", "Add execute/read/write breakpoint over a logical address range."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"start_address", {
                    {"type", "string"},
                    {"description", "Start logical hex address; ranges: rom_ram 0000-FFFF, vram 0000-3FFF, vdp_reg 00-07."}
                }},
                {"end_address", {
                    {"type", "string"},
                    {"description", "End logical hex address; same ranges as start_address."}
                }},
                {"memory_area", {
                    {"type", "string"},
                    {"description", "Memory area: rom_ram, vram, vdp_reg."},
                    {"enum", json::array({"rom_ram", "vram", "vdp_reg"})}
                }},
                {"read", {
                    {"type", "boolean"},
                    {"description", "Read access breakpoint; PC stops after the access. Default false."}
                }},
                {"write", {
                    {"type", "boolean"},
                    {"description", "Write access breakpoint; PC stops after the access. Default false."}
                }},
                {"execute", {
                    {"type", "boolean"},
                    {"description", "Execution breakpoint; only valid for rom_ram. Default true."}
                }}
            }},
            {"required", json::array({"start_address", "end_address"})}
        }}
    });

    tools.push_back({
        {"name", "remove_breakpoint"},
        {"title", "Remove Breakpoint"},
        {"description", "Remove matching single/range breakpoint by address, end_address, and memory area."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"address", {
                    {"type", "string"},
                    {"description", "Logical address hex; range removals use this as start."}
                }},
                {"end_address", {
                    {"type", "string"},
                    {"description", "Range end address hex; required only for range breakpoints."}
                }},
                {"memory_area", {
                    {"type", "string"},
                    {"description", "Memory area: rom_ram default, vram, vdp_reg."},
                    {"enum", json::array({"rom_ram", "vram", "vdp_reg"})}
                }}
            }},
            {"required", json::array({"address"})}
        }}
    });

    tools.push_back({
        {"name", "list_breakpoints"},
        {"title", "List Breakpoints"},
        {"description", "List all execution/read/write breakpoints."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "toggle_irq_breakpoints"},
        {"title", "Toggle IRQ Breakpoints"},
        {"description", "Enable/disable interrupt breakpoints for RESET, NMI, INT."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"enabled", {
                    {"type", "boolean"},
                    {"description", "true breaks on IRQ, false disables."}
                }}
            }},
            {"required", json::array({"enabled"})}
        }}
    });

    // Memory tools
    tools.push_back({
        {"name", "list_memory_areas"},
        {"title", "List Memory Areas"},
        {"description", "List memory spaces/tabs: WRAM, VRAM, ROM banks; returns area IDs, sizes, offsets."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "read_memory"},
        {"title", "Read Memory"},
        {"description", "Read bytes from memory area/tab by physical 0-based offset."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }},
                {"offset", {
                    {"type", "string"},
                    {"description", "0-based hex offset in area, e.g. '0100'."}
                }},
                {"size", {
                    {"type", "integer"},
                    {"description", "Number of bytes to read."}
                }}
            }},
            {"required", json::array({"area", "offset", "size"})}
        }}
    });

    tools.push_back({
        {"name", "write_memory"},
        {"title", "Write Memory"},
        {"description", "Write hex bytes to memory area/tab by physical 0-based offset."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }},
                {"offset", {
                    {"type", "string"},
                    {"description", "0-based hex offset in area, e.g. '0100'."}
                }},
                {"bytes", {
                    {"type", "string"},
                    {"description", "Hex bytes, spaces optional, e.g. 'A9 00 85 10'."}
                }}
            }},
            {"required", json::array({"area", "offset", "bytes"})}
        }}
    });

    // Register tools
    tools.push_back({
        {"name", "write_z80_register"},
        {"title", "Write Z80 Register"},
        {"description", "Write Z80 CPU register, alternate register, index register, SP/PC/WZ/I/R."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"name", {
                    {"type", "string"},
                    {"description", "Register: AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, SP, PC, WZ, A, F, B, C, D, E, H, L, I, R."}
                }},
                {"value", {
                    {"type", "string"},
                    {"description", "Hex value."}
                }}
            }},
            {"required", json::array({"name", "value"})}
        }}
    });

    // Disassembly tool
    tools.push_back({
        {"name", "get_disassembly"},
        {"title", "Get Disassembly"},
        {"description", "Read recorded Z80 disassembly for logical range: bank, segment, mnemonic, bytes. Records exist after execution; max practical range 0x2000."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"start_address", {
                    {"type", "string"},
                    {"description", "Start logical address hex: 'E177', '0xE177', or '$E177'."}
                }},
                {"end_address", {
                    {"type", "string"},
                    {"description", "End logical address hex; must be >= start_address."}
                }},
                {"bank", {
                    {"type", "string"},
                    {"description", "Optional ROM bank 00-FF; read records from that bank instead of current map."}
                }},
                {"resolve_symbols", {
                    {"type", "boolean"},
                    {"description", "Resolve addresses to symbols when available. Default false."}
                }},
                {"detailed", {
                    {"type", "boolean"},
                    {"description", "Include opcode bytes, jump targets, IRQ metadata. Default false compact output."}
                }}
            }},
            {"required", json::array({"start_address", "end_address"})}
        }}
    });

    // Media info tool
    tools.push_back({
        {"name", "get_media_info"},
        {"title", "Get Media Info"},
        {"description", "Read loaded ColecoVision ROM info: path, type, size, mapper."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "list_recent_media"},
        {"title", "List Recent Media"},
        {"description", "List recent ROMs with file_path values for load_media."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    // Chip status tools
    tools.push_back({
        {"name", "get_z80_status"},
        {"title", "Get Z80 Status"},
        {"description", "Read Z80 CPU state: registers, flags, interrupts, HALT, interrupt mode."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "get_vdp_registers"},
        {"title", "Get VDP Registers"},
        {"description", "Read TMS9918 VDP registers R0-R7 with hex values and decoded meanings."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "get_vdp_status"},
        {"title", "Get VDP Status"},
        {"description", "Read TMS9918 VDP video state: flags, mode, render line, display state."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "get_psg_status"},
        {"title", "Get PSG Status"},
        {"description", "Read SN76489 PSG audio state: 3 tone channels, noise, volume, period, frequency."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "get_ay8910_status"},
        {"title", "Get AY-3-8910 Status"},
        {"description", "Read AY-3-8910 SGM audio state: 3 channels, mixer, noise, envelope, registers, mute."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "get_screenshot"},
        {"title", "Get Screenshot"},
        {"description", "Capture current screen/frame/video output as PNG screenshot image."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    // Media and state management tools
    tools.push_back({
        {"name", "load_media"},
        {"title", "Load Media"},
        {"description", "Load ROM media (.col .cv .rom .bin .zip), auto-load .sym, reset emulator."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"file_path", {
                    {"type", "string"},
                    {"description", "Absolute ROM file path."}
                }}
            }},
            {"required", json::array({"file_path"})}
        }}
    });

    tools.push_back({
        {"name", "load_symbols"},
        {"title", "Load Symbols"},
        {"description", "Load .sym debug symbols (BANK:ADDRESS LABEL); append to symbol table."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"file_path", {
                    {"type", "string"},
                    {"description", "Absolute symbol file path."}
                }}
            }},
            {"required", json::array({"file_path"})}
        }}
    });

    tools.push_back({
        {"name", "list_save_state_slots"},
        {"title", "List Save State Slots"},
        {"description", "List save-state slots: slot, ROM name, timestamp, screenshot flag."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "select_save_state_slot"},
        {"title", "Select Save State Slot"},
        {"description", "Select active save-state slot 1-5 for save_state/load_state."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"slot", {
                    {"type", "integer"},
                    {"description", "Slot number 1-5."},
                    {"minimum", 1},
                    {"maximum", 5}
                }}
            }},
            {"required", json::array({"slot"})}
        }}
    });

    tools.push_back({
        {"name", "save_state"},
        {"title", "Save State"},
        {"description", "Save emulator state to active save-state slot."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "load_state"},
        {"title", "Load State"},
        {"description", "Load emulator state from active save-state slot."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "set_fast_forward_speed"},
        {"title", "Set Fast Forward Speed"},
        {"description", "Set fast-forward speed index: 0=1.5x, 1=2x, 2=2.5x, 3=3x, 4=unlimited."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"speed", {
                    {"type", "integer"},
                    {"description", "Speed index 0-4."},
                    {"minimum", 0},
                    {"maximum", 4}
                }}
            }},
            {"required", json::array({"speed"})}
        }}
    });

    tools.push_back({
        {"name", "toggle_fast_forward"},
        {"title", "Toggle Fast Forward"},
        {"description", "Enable/disable fast-forward mode at configured speed."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"enabled", {
                    {"type", "boolean"},
                    {"description", "true enables fast forward; false disables."}
                }}
            }},
            {"required", json::array({"enabled"})}
        }}
    });

    tools.push_back({
        {"name", "get_rewind_status"},
        {"title", "Get Rewind Status"},
        {"description", "Read rewind buffer: snapshot count, capacity, memory usage."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()}
        }}
    });

    tools.push_back({
        {"name", "rewind_seek"},
        {"title", "Rewind Seek"},
        {"description", "Load rewind snapshot by index; 0 is most recent."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"snapshot", {
                    {"type", "integer"},
                    {"description", "Snapshot index; 0 is most recent."},
                    {"minimum", 0}
                }}
            }},
            {"required", json::array({"snapshot"})}
        }}
    });

    // Controller input tools
    tools.push_back({
        {"name", "controller_button"},
        {"title", "Controller Button"},
        {"description", "Press/release/tap Coleco controller/keypad for players 1-2: directions, 0-9, *, #, fire/color aliases."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"player", {
                    {"type", "integer"},
                    {"description", "Player number 1-2."},
                    {"minimum", 1},
                    {"maximum", 2}
                }},
                {"button", {
                    {"type", "string"},
                    {"description", "Button: directions, 0-9, asterisk/*, hash/#, left/right_button, yellow/red, fire1/fire2, blue/purple."},
                    {"enum", json::array({"up", "down", "left", "right", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "asterisk", "*", "hash", "#", "left_button", "right_button", "yellow", "red", "fire1", "fire2", "blue", "purple"})}
                }},
                {"action", {
                    {"type", "string"},
                    {"description", "Action: press, release, or press_and_release tap."},
                    {"enum", json::array({"press", "release", "press_and_release"})}
                }}
            }},
            {"required", json::array({"player", "button", "action"})}
        }}
    });

    tools.push_back({
        {"name", "controller_macro"},
        {"title", "Controller Macro"},
        {"description", "Run a frame-based controller macro. Commands are tap, press, release, and wait; player defaults to 1."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"player", {
                    {"type", "integer"},
                    {"description", "Default player number 1-2."},
                    {"minimum", 1},
                    {"maximum", 2}
                }},
                {"commands", {
                    {"type", "array"},
                    {"description", "Ordered macro commands, e.g. [{\"tap\":\"1\"},{\"wait\":30},{\"press\":\"right\"},{\"wait\":60},{\"release\":\"right\"}]."},
                    {"minItems", 1},
                    {"items", {
                        {"type", "object"},
                        {"properties", {
                            {"tap", {
                                {"type", "string"},
                                {"description", "Tap button for one frame."},
                                {"enum", json::array({"up", "down", "left", "right", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "asterisk", "*", "hash", "#", "left_button", "right_button", "yellow", "red", "fire1", "fire2", "blue", "purple"})}
                            }},
                            {"press", {
                                {"type", "string"},
                                {"description", "Press and hold button."},
                                {"enum", json::array({"up", "down", "left", "right", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "asterisk", "*", "hash", "#", "left_button", "right_button", "yellow", "red", "fire1", "fire2", "blue", "purple"})}
                            }},
                            {"release", {
                                {"type", "string"},
                                {"description", "Release button."},
                                {"enum", json::array({"up", "down", "left", "right", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "asterisk", "*", "hash", "#", "left_button", "right_button", "yellow", "red", "fire1", "fire2", "blue", "purple"})}
                            }},
                            {"wait", {
                                {"type", "integer"},
                                {"description", "Frames to wait."},
                                {"minimum", 1},
                                {"maximum", 1000}
                            }},
                            {"player", {
                                {"type", "integer"},
                                {"description", "Player override for this command, 1-2."},
                                {"minimum", 1},
                                {"maximum", 2}
                            }}
                        }},
                        {"additionalProperties", false}
                    }}
                }}
            }},
            {"required", json::array({"commands"})},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "list_sprites"},
        {"title", "List Sprites"},
        {"description", "List hardware sprites: position, size, pattern index."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "get_sprite_image"},
        {"title", "Get Sprite Image"},
        {"description", "Capture one hardware sprite as PNG image."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"sprite_index", {
                    {"type", "integer"},
                    {"description", "Sprite index 0-31."}
                }}
            }},
            {"required", json::array({"sprite_index"})}
        }}
    });

    // Disassembler tools
    tools.push_back({
        {"name", "debug_run_to_cursor"},
        {"title", "Debug Run To Cursor"},
        {"description", "Continue execution until logical address."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"address", {
                    {"type", "string"},
                    {"description", "Logical hex address, e.g. 'E177'."}
                }}
            }},
            {"required", json::array({"address"})}
        }}
    });

    tools.push_back({
        {"name", "add_disassembler_bookmark"},
        {"title", "Add Disassembler Bookmark"},
        {"description", "Add disassembler bookmark at logical address."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"address", {
                    {"type", "string"},
                    {"description", "Logical hex address, e.g. 'E177'."}
                }},
                {"name", {
                    {"type", "string"},
                    {"description", "Bookmark name; optional, auto-generated if omitted."}
                }}
            }},
            {"required", json::array({"address"})}
        }}
    });

    tools.push_back({
        {"name", "remove_disassembler_bookmark"},
        {"title", "Remove Disassembler Bookmark"},
        {"description", "Remove disassembler bookmark at logical address."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"address", {
                    {"type", "string"},
                    {"description", "Logical hex address, e.g. 'E177'."}
                }}
            }},
            {"required", json::array({"address"})}
        }}
    });

    tools.push_back({
        {"name", "add_symbol"},
        {"title", "Add Symbol"},
        {"description", "Add disassembler symbol/label at bank:logical address."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"bank", {
                    {"type", "string"},
                    {"description", "Bank hex byte, e.g. '00'."}
                }},
                {"address", {
                    {"type", "string"},
                    {"description", "Logical hex address, e.g. 'E177'."}
                }},
                {"name", {
                    {"type", "string"},
                    {"description", "Symbol/label name."}
                }}
            }},
            {"required", json::array({"bank", "address", "name"})}
        }}
    });

    tools.push_back({
        {"name", "remove_symbol"},
        {"title", "Remove Symbol"},
        {"description", "Remove disassembler symbol/label at bank:logical address."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"bank", {
                    {"type", "string"},
                    {"description", "Bank hex byte, e.g. '00'."}
                }},
                {"address", {
                    {"type", "string"},
                    {"description", "Logical hex address, e.g. 'E177'."}
                }}
            }},
            {"required", json::array({"bank", "address"})}
        }}
    });

    // Memory editor tools
    tools.push_back({
        {"name", "select_memory_range"},
        {"title", "Select Memory Range"},
        {"description", "Select memory editor range by area and 0-based offsets."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }},
                {"start_address", {
                    {"type", "string"},
                    {"description", "Start 0-based hex offset, e.g. '0100'."}
                }},
                {"end_address", {
                    {"type", "string"},
                    {"description", "End 0-based hex offset, e.g. '01FF'."}
                }}
            }},
            {"required", json::array({"area", "start_address", "end_address"})}
        }}
    });

    tools.push_back({
        {"name", "set_memory_selection_value"},
        {"title", "Set Memory Selection Value"},
        {"description", "Fill current memory selection with byte value; use select_memory_range first."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }},
                {"value", {
                    {"type", "string"},
                    {"description", "Byte hex value, e.g. 'FF' or '00'."}
                }}
            }},
            {"required", json::array({"area", "value"})}
        }}
    });

    tools.push_back({
        {"name", "add_memory_bookmark"},
        {"title", "Add Memory Bookmark"},
        {"description", "Add memory bookmark at area offset."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }},
                {"address", {
                    {"type", "string"},
                    {"description", "0-based hex offset in physical memory area."}
                }},
                {"name", {
                    {"type", "string"},
                    {"description", "Bookmark name; optional."}
                }}
            }},
            {"required", json::array({"area", "address"})}
        }}
    });

    tools.push_back({
        {"name", "remove_memory_bookmark"},
        {"title", "Remove Memory Bookmark"},
        {"description", "Remove memory bookmark at area offset."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }},
                {"address", {
                    {"type", "string"},
                    {"description", "0-based hex offset in physical memory area."}
                }}
            }},
            {"required", json::array({"area", "address"})}
        }}
    });

    tools.push_back({
        {"name", "add_memory_watch"},
        {"title", "Add Memory Watch"},
        {"description", "Add memory watch at area offset with optional notes and bit size."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }},
                {"address", {
                    {"type", "string"},
                    {"description", "0-based hex offset in physical memory area."}
                }},
                {"notes", {
                    {"type", "string"},
                    {"description", "Watch notes; optional."}
                }},
                {"size", {
                    {"type", "integer"},
                    {"description", "Watch bit size: 8, 16, 24, or 32; default 8."},
                    {"enum", {8, 16, 24, 32}}
                }}
            }},
            {"required", json::array({"area", "address"})}
        }}
    });

    tools.push_back({
        {"name", "remove_memory_watch"},
        {"title", "Remove Memory Watch"},
        {"description", "Remove memory watch at area offset."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }},
                {"address", {
                    {"type", "string"},
                    {"description", "0-based hex offset in physical memory area."}
                }}
            }},
            {"required", json::array({"area", "address"})}
        }}
    });

    tools.push_back({
        {"name", "list_disassembler_bookmarks"},
        {"title", "List Disassembler Bookmarks"},
        {"description", "List disassembler bookmarks."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "list_symbols"},
        {"title", "List Symbols"},
        {"description", "List disassembler symbols/labels."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "get_call_stack"},
        {"title", "Get Call Stack"},
        {"description", "List current call stack/subroutine hierarchy."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", json::object()},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "list_memory_bookmarks"},
        {"title", "List Memory Bookmarks"},
        {"description", "List bookmarks for memory area."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }}
            }},
            {"required", json::array({"area"})}
        }}
    });

    tools.push_back({
        {"name", "list_memory_watches"},
        {"title", "List Memory Watches"},
        {"description", "List watches for memory area."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }}
            }},
            {"required", json::array({"area"})}
        }}
    });

    tools.push_back({
        {"name", "get_memory_selection"},
        {"title", "Get Memory Selection"},
        {"description", "Read current memory selection range for area."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }}
            }},
            {"required", json::array({"area"})}
        }}
    });

    tools.push_back({
        {"name", "memory_search_capture"},
        {"title", "Memory Search Capture"},
        {"description", "Snapshot memory area for later value-change search."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }}
            }},
            {"required", json::array({"area"})}
        }}
    });

    tools.push_back({
        {"name", "memory_search"},
        {"title", "Memory Search"},
        {"description", "Search memory values by comparison against snapshot, constant value, or address."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas"}
                }},
                {"operator", {
                    {"type", "string"},
                    {"description", "Comparison operator: <, >, ==, !=, <=, >=."},
                    {"enum", json::array({"<", ">", "==", "!=", "<=", ">="})}
                }},
                {"compare_type", {
                    {"type", "string"},
                    {"description", "Compare against previous snapshot, constant value, or value at address."},
                    {"enum", json::array({"previous", "value", "address"})}
                }},
                {"compare_value", {
                    {"type", "integer"},
                    {"description", "Search value or address used for compare_type value/address."}
                }},
                {"data_type", {
                    {"type", "string"},
                    {"description", "Value type: unsigned default, signed, or hex."},
                    {"enum", json::array({"unsigned", "signed", "hex"})}
                }}
            }},
            {"required", json::array({"area", "operator", "compare_type"})}
        }}
    });

    tools.push_back({
        {"name", "memory_find_bytes"},
        {"title", "Find Byte Sequence in Memory"},
        {"description", "Find consecutive hex byte sequence in memory; return addresses."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"area", {
                    {"type", "integer"},
                    {"description", "Memory area ID from list_memory_areas."}
                }},
                {"hex_bytes", {
                    {"type", "string"},
                    {"description", "Hex byte pairs to find, e.g. '04E5FF32' (spaces optional)"}
                }}
            }},
            {"required", json::array({"area", "hex_bytes"})}
        }}
    });

    // Tracing tools
    tools.push_back({
        {"name", "get_trace_log"},
        {"title", "Get Trace Log"},
        {"description", "Read trace log entries: CPU, IRQ, VDP, PSG, AY-3-8910, I/O port, SGM events."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"start", {
                    {"type", "integer"},
                    {"description", "Start index; 0 oldest, default 0."},
                    {"minimum", 0}
                }},
                {"count", {
                    {"type", "integer"},
                    {"description", "Entry count; default 100, max 1000."},
                    {"minimum", 1},
                    {"maximum", 1000}
                }}
            }}
        }}
    });

    tools.push_back({
        {"name", "set_trace_log"},
        {"title", "Set Trace Log"},
        {"description", "Enable/disable trace log with flags bitmask: 0 cpu, 1 irq, 2 vdp_write, 3 vdp_status, 4 psg, 5 ay8910, 6 io_port, 7 sgm; 0xFF all."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"enabled", {
                    {"type", "boolean"},
                    {"description", "true starts tracing, false stops."}
                }},
                {"flags", {
                    {"type", "integer"},
                    {"description", "Trace type bitmask; default 0xFF all."},
                    {"minimum", 0},
                    {"maximum", 255}
                }}
            }},
            {"required", json::array({"enabled"})}
        }}
    });

    return tools;
}

void McpServer::HandleToolsList(const json& request)
{
    if (!request.contains("id"))
    {
        SendError(0, -32600, "Invalid Request: missing id");
        return;
    }

    int64_t id = request["id"];

    json tools = BuildToolList();

    m_toolRegistry.SetTools(tools);

    if (!g_mcp_router_disabled)
    {
        json visibleTools = m_toolRegistry.GetDirectTools();
        AddRouterTools(visibleTools);
        tools = visibleTools;
    }

    json response;
    response["jsonrpc"] = "2.0";
    response["id"] = id;
    response["result"] = {
        {"tools", tools}
    };

    SendResponse(response);
}

void McpServer::EnsureToolRegistry()
{
    if (!m_toolRegistry.IsEmpty())
        return;

    m_toolRegistry.SetTools(BuildToolList());
}


void McpServer::AddRouterTools(json& tools)
{
    tools.push_back({
        {"name", "list_tool_categories"},
        {"title", "List Tool Categories"},
        {"description", "List routed MCP tool categories with descriptions and tool counts. Use this first to discover advanced emulator/debugger tools."},
        {"inputSchema", {
            {"type", "object"},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "get_category_tools"},
        {"title", "Get Category Tools"},
        {"description", "List routed tools in a category with compact descriptions. Use category names returned by list_tool_categories, then call get_tool_info for one tool's input schema."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"category", {{"type", "string"}}}
            }},
            {"required", json::array({"category"})},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "get_tool_info"},
        {"title", "Get Tool Info"},
        {"description", "Return one MCP tool's title, description, category, direct/routed status, and real input schema. Use this after search_tools or get_category_tools before execute_tool."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"name", {{"type", "string"}}}
            }},
            {"required", json::array({"name"})},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "search_tools"},
        {"title", "Search Tools"},
        {"description", "Search direct and routed MCP tools by keyword, category, title, description, and aliases. Use this when you know what you want to do but not the tool name."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"query", {{"type", "string"}}}
            }},
            {"required", json::array({"query"})},
            {"additionalProperties", false}
        }}
    });

    tools.push_back({
        {"name", "execute_tool"},
        {"title", "Execute Routed Tool"},
        {"description", "Execute a routed MCP tool by name with arguments. Use get_category_tools or search_tools first to discover the tool name and input schema."},
        {"inputSchema", {
            {"type", "object"},
            {"properties", {
                {"name", {{"type", "string"}}},
                {"arguments", {
                    {"type", "object"},
                    {"additionalProperties", true}
                }}
            }},
            {"required", json::array({"name"})},
            {"additionalProperties", false}
        }}
    });
}

json McpServer::HandleRouterListCategories()
{
    EnsureToolRegistry();

    json stats = m_toolRegistry.GetStats();
    stats["categories"] = m_toolRegistry.GetCategories();

    return stats;
}

json McpServer::HandleRouterGetCategoryTools(const json& arguments)
{
    EnsureToolRegistry();

    std::string category = arguments.value("category", "");

    if (!m_toolRegistry.HasCategory(category))
    {
        return {
            {"error", "Unknown category"},
            {"category", category},
            {"available_categories", m_toolRegistry.GetCategoryNames()}
        };
    }

    return {
        {"category", category},
        {"title", m_toolRegistry.GetCategoryTitle(category)},
        {"description", m_toolRegistry.GetCategoryDescription(category)},
        {"tool_count", m_toolRegistry.GetCategoryToolCount(category)},
        {"tools", m_toolRegistry.GetToolsInCategory(category)}
    };
}

json McpServer::HandleRouterSearchTools(const json& arguments)
{
    EnsureToolRegistry();

    std::string query = arguments.value("query", "");
    json tools = m_toolRegistry.SearchTools(query);

    return {
        {"query", query},
        {"count", tools.size()},
        {"limit", m_toolRegistry.GetSearchToolLimit()},
        {"matches", tools}
    };
}

json McpServer::HandleRouterGetToolInfo(const json& arguments)
{
    EnsureToolRegistry();

    std::string tool_name = arguments.value("name", "");
    json tool = m_toolRegistry.GetToolInfo(tool_name);

    if (tool.empty())
    {
        return {
            {"error", "Unknown tool"},
            {"name", tool_name},
            {"hint", "Use search_tools or get_category_tools to discover available tool names."}
        };
    }

    return tool;
}

void McpServer::SendToolResult(int64_t id, const json& result)
{
    json response;
    response["jsonrpc"] = "2.0";
    response["id"] = id;
    response["result"] = {
        {"content", json::array({
            {
                {"type", "text"},
                {"text", result.dump(2, ' ', false, json::error_handler_t::replace)}
            }
        })}
    };

    SendResponse(response);
}

void McpServer::HandleToolsCall(const json& request)
{
    if (!request.contains("id"))
    {
        SendError(0, -32600, "Invalid Request: missing id");
        return;
    }

    int64_t id = request["id"];

    if (!request.contains("params") || !request["params"].contains("name"))
    {
        SendError(id, -32602, "Invalid params: missing tool name");
        return;
    }

    std::string toolName = request["params"]["name"];
    json arguments = request["params"].contains("arguments") ? request["params"]["arguments"] : json::object();

    if (!g_mcp_router_disabled && m_toolRegistry.IsRouterTool(toolName))
        EnsureToolRegistry();

    if (!g_mcp_router_disabled && m_toolRegistry.IsRouterTool(toolName, "list_tool_categories"))
    {
        SendToolResult(id, HandleRouterListCategories());
        return;
    }

    if (!g_mcp_router_disabled && m_toolRegistry.IsRouterTool(toolName, "get_category_tools"))
    {
        SendToolResult(id, HandleRouterGetCategoryTools(arguments));
        return;
    }

    if (!g_mcp_router_disabled && m_toolRegistry.IsRouterTool(toolName, "get_tool_info"))
    {
        SendToolResult(id, HandleRouterGetToolInfo(arguments));
        return;
    }

    if (!g_mcp_router_disabled && m_toolRegistry.IsRouterTool(toolName, "search_tools"))
    {
        SendToolResult(id, HandleRouterSearchTools(arguments));
        return;
    }

    if (!g_mcp_router_disabled && m_toolRegistry.IsRouterTool(toolName, "execute_tool"))
    {
        if (!arguments.contains("name") || !arguments["name"].is_string())
        {
            SendError(id, -32602, "Invalid params: missing routed tool name");
            return;
        }

        toolName = arguments["name"].get<std::string>();

        if (!m_toolRegistry.HasTool(toolName))
        {
            SendToolResult(id, {{"error", "Unknown tool"}, {"name", toolName}});
            return;
        }

        if (arguments.contains("arguments") && arguments["arguments"].is_object())
            arguments = arguments["arguments"];
        else
            arguments = json::object();
    }

    // Enqueue command for main thread to execute
    DebugCommand* cmd = new DebugCommand();
    cmd->requestId = id;
    cmd->toolName = toolName;
    cmd->arguments = arguments;
    m_commandQueue.Push(cmd);
}

static int GetBreakpointTypeFromString(const std::string& memory_area)
{
    if (memory_area == "rom_ram") return Processor::GC_BREAKPOINT_TYPE_ROMRAM;
    if (memory_area == "vram") return Processor::GC_BREAKPOINT_TYPE_VRAM;
    if (memory_area == "vdp_reg") return Processor::GC_BREAKPOINT_TYPE_VDP_REGISTER;
    return Processor::GC_BREAKPOINT_TYPE_ROMRAM; // default
}

static bool IsBreakpointMemoryAreaValid(const std::string& memory_area)
{
    return memory_area == "rom_ram" || memory_area == "vram" || memory_area == "vdp_reg";
}

json McpServer::ExecuteCommand(const std::string& toolName, const json& arguments)
{
    // Normalize tool name: VS Code converts underscores to dots
    std::string normalizedTool = toolName;
    size_t pos = 0;
    while ((pos = normalizedTool.find('.', pos)) != std::string::npos) {
        normalizedTool[pos] = '_';
        pos++;
    }

    // Execution control
    if (normalizedTool == "debug_pause")
    {
        m_debugAdapter.Pause();
        return {{"success", true}};
    }
    else if (normalizedTool == "debug_continue")
    {
        m_debugAdapter.Resume();
        return {{"success", true}};
    }
    else if (normalizedTool == "debug_step_into")
    {
        m_debugAdapter.StepInto();
        return {{"success", true}};
    }
    else if (normalizedTool == "debug_step_over")
    {
        m_debugAdapter.StepOver();
        return {{"success", true}};
    }
    else if (normalizedTool == "debug_step_out")
    {
        m_debugAdapter.StepOut();
        return {{"success", true}};
    }
    else if (normalizedTool == "debug_step_frame")
    {
        int frames = arguments.value("frames", 1);

        if (frames < 1 || frames > 1000)
            return {{"error", "Invalid frames value (must be 1-1000)"}};

        m_debugAdapter.StepFrame(frames);
        return {{"success", true}, {"frames", frames}};
    }
    else if (normalizedTool == "debug_reset")
    {
        m_debugAdapter.Reset();
        return {{"success", true}};
    }
    else if (normalizedTool == "debug_get_status")
    {
        return m_debugAdapter.GetDebugStatus();
    }
    // Breakpoints
    else if (normalizedTool == "set_breakpoint")
    {
        std::string addrStr = arguments["address"];
        u16 address;
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};

        std::string memory_area = arguments.value("memory_area", "rom_ram");
        if (!IsBreakpointMemoryAreaValid(memory_area))
            return {{"error", "Invalid memory_area (must be: rom_ram, vram, vdp_reg)"}};
        int breakpoint_type = GetBreakpointTypeFromString(memory_area);

        bool read = arguments.value("read", false);
        bool write = arguments.value("write", false);
        bool execute = arguments.value("execute", true);

        if (breakpoint_type != 0)
            execute = false;

        if (!read && !write && !execute)
            return {{"error", "At least one of read, write, or execute must be true"}};

        u16 max_address = 0xFFFF;
        if (breakpoint_type == Processor::GC_BREAKPOINT_TYPE_VRAM)
            max_address = 0x3FFF;
        else if (breakpoint_type == Processor::GC_BREAKPOINT_TYPE_VDP_REGISTER)
            max_address = 0x0007;
        if (address > max_address)
        {
            char msg[128];
            snprintf(msg, sizeof(msg), "Address 0x%04X out of range for %s (max: 0x%04X)", address, memory_area.c_str(), max_address);
            return {{"error", msg}};
        }

        m_debugAdapter.SetBreakpoint(address, breakpoint_type, read, write, execute);
        return {{"success", true}, {"address", addrStr}, {"memory_area", memory_area}};
    }
    else if (normalizedTool == "set_breakpoint_range")
    {
        std::string startAddrStr = arguments["start_address"];
        std::string endAddrStr = arguments["end_address"];
        u16 start_address, end_address;

        if (!parse_mcp_hex_with_prefix(startAddrStr, &start_address))
            return {{"error", "Invalid start_address format"}};
        if (!parse_mcp_hex_with_prefix(endAddrStr, &end_address))
            return {{"error", "Invalid end_address format"}};
        if (start_address > end_address)
            return {{"error", "start_address must be <= end_address"}};

        std::string memory_area = arguments.value("memory_area", "rom_ram");
        if (!IsBreakpointMemoryAreaValid(memory_area))
            return {{"error", "Invalid memory_area (must be: rom_ram, vram, vdp_reg)"}};
        int breakpoint_type = GetBreakpointTypeFromString(memory_area);

        bool read = arguments.value("read", false);
        bool write = arguments.value("write", false);
        bool execute = arguments.value("execute", true);

        if (breakpoint_type != 0)
            execute = false;

        if (!read && !write && !execute)
            return {{"error", "At least one of read, write, or execute must be true"}};

        u16 max_address = 0xFFFF;
        if (breakpoint_type == Processor::GC_BREAKPOINT_TYPE_VRAM)
            max_address = 0x3FFF;
        else if (breakpoint_type == Processor::GC_BREAKPOINT_TYPE_VDP_REGISTER)
            max_address = 0x0007;
        if (start_address > max_address || end_address > max_address)
        {
            char msg[128];
            snprintf(msg, sizeof(msg), "Address out of range for %s (max: 0x%04X)", memory_area.c_str(), max_address);
            return {{"error", msg}};
        }

        m_debugAdapter.SetBreakpointRange(start_address, end_address, breakpoint_type,
                                         read, write, execute);
        return {{"success", true}, {"start_address", startAddrStr}, {"end_address", endAddrStr}, {"memory_area", memory_area}};
    }
    else if (normalizedTool == "remove_breakpoint")
    {
        std::string addrStr = arguments["address"];
        u16 address;
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};

        std::string memory_area = arguments.value("memory_area", "rom_ram");
        if (!IsBreakpointMemoryAreaValid(memory_area))
            return {{"error", "Invalid memory_area (must be: rom_ram, vram, vdp_reg)"}};
        int breakpoint_type = GetBreakpointTypeFromString(memory_area);

        // Check if end_address is provided for range breakpoints
        u16 end_address = 0;
        if (arguments.contains("end_address"))
        {
            std::string endAddrStr = arguments["end_address"];
            if (!parse_mcp_hex_with_prefix(endAddrStr, &end_address))
                return {{"error", "Invalid end_address format"}};
        }

        m_debugAdapter.ClearBreakpointByAddress(address, breakpoint_type, end_address);
        return {{"success", true}, {"address", addrStr}, {"memory_area", memory_area}};
    }
    else if (normalizedTool == "list_breakpoints")
    {
        std::vector<BreakpointInfo> breakpoints = m_debugAdapter.ListBreakpoints();
        json bpArray = json::array();
        for (const BreakpointInfo& bp : breakpoints)
        {
            json bpObj;
            bpObj["enabled"] = bp.enabled;
            bpObj["type"] = bp.type_name;

            std::ostringstream addr_ss;
            addr_ss << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << bp.address1;
            bpObj["address"] = addr_ss.str();

            if (bp.range)
            {
                std::ostringstream addr2_ss;
                addr2_ss << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << bp.address2;
                bpObj["address2"] = addr2_ss.str();
            }

            bpObj["read"] = bp.read;
            bpObj["write"] = bp.write;
            bpObj["execute"] = bp.execute;
            bpArray.push_back(bpObj);
        }
        return {{"breakpoints", bpArray}, {"irq_breakpoints_enabled", (bool)emu_debug_irq_breakpoints}};
    }
    else if (normalizedTool == "toggle_irq_breakpoints")
    {
        bool enabled = arguments["enabled"];
        emu_debug_irq_breakpoints = enabled;
        return {{"success", true}, {"irq_breakpoints_enabled", enabled}};
    }
    // Memory
    else if (normalizedTool == "list_memory_areas")
    {
        std::vector<MemoryAreaInfo> areas = m_debugAdapter.ListMemoryAreas();
        json areaArray = json::array();
        for (const MemoryAreaInfo& area : areas)
        {
            json areaObj;
            areaObj["id"] = area.id;
            areaObj["name"] = area.name;
            areaObj["size"] = area.size;
            areaObj["display_base"] = area.display_base;
            areaArray.push_back(areaObj);
        }
        return {{"areas", areaArray}};
    }
    else if (normalizedTool == "read_memory")
    {
        int area = arguments["area"];
        std::string offsetStr = arguments["offset"];
        u32 offset;
        if (!parse_mcp_hex_with_prefix(offsetStr, &offset))
            return {{"error", "Invalid offset format"}};

        size_t size = arguments["size"];
        std::vector<u8> data = m_debugAdapter.ReadMemoryArea(area, offset, size);

        std::ostringstream hex_ss;
        for (size_t i = 0; i < data.size(); i++)
        {
            hex_ss << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << (int)data[i];
            if (i < data.size() - 1)
                hex_ss << " ";
        }

        return {{"area", area}, {"offset", offsetStr}, {"data", hex_ss.str()}};
    }
    else if (normalizedTool == "write_memory")
    {
        int area = arguments["area"];
        std::string offsetStr = arguments["offset"];
        u32 offset;
        if (!parse_mcp_hex_with_prefix(offsetStr, &offset))
            return {{"error", "Invalid offset format"}};

        std::string bytesStr = arguments["bytes"];
        std::vector<u8> data;

        // Parse hex bytes
        std::istringstream iss(bytesStr);
        std::string byteStr;
        while (iss >> byteStr)
        {
            u8 byte;
            if (!parse_mcp_hex_with_prefix(byteStr, &byte))
                return {{"error", "Invalid byte format"}};
            data.push_back(byte);
        }

        m_debugAdapter.WriteMemoryArea(area, offset, data);
        return {{"success", true}, {"area", area}, {"offset", offsetStr}, {"bytes_written", data.size()}};
    }
    // Registers
    else if (normalizedTool == "write_z80_register")
    {
        std::string name = arguments["name"];
        std::string valueStr = arguments["value"];
        u32 value;
        if (!parse_mcp_hex_with_prefix(valueStr, &value))
            return {{"error", "Invalid value format"}};

        m_debugAdapter.SetRegister(name, value);
        return {{"success", true}, {"register", name}, {"value", valueStr}};
    }
    // Disassembly
    else if (normalizedTool == "get_disassembly")
    {
        if (!arguments.contains("start_address"))
            return {{"error", "start_address is required"}};
        if (!arguments.contains("end_address"))
            return {{"error", "end_address is required"}};

        std::string startAddrStr = arguments["start_address"];
        std::string endAddrStr = arguments["end_address"];
        u16 start_address, end_address;

        if (!parse_mcp_hex_with_prefix(startAddrStr, &start_address))
            return {{"error", "Invalid start_address format"}};
        if (!parse_mcp_hex_with_prefix(endAddrStr, &end_address))
            return {{"error", "Invalid end_address format"}};
        if (start_address > end_address)
            return {{"error", "start_address must be <= end_address"}};

        u32 range_size = (u32)end_address - (u32)start_address + 1;
        if (range_size > 0x2000)
            return {{"error", "Address range too large. Maximum range is 0x2000 (8KB). Use smaller ranges for disassembly."}};

        // Optional bank parameter (-1 means use current mapper mappings)
        int bank = -1;
        if (arguments.contains("bank"))
        {
            std::string bankStr = arguments["bank"];
            u8 bank_value;
            if (!parse_mcp_hex_with_prefix(bankStr, &bank_value))
                return {{"error", "Invalid bank format (must be 00-FF in hex)"}};
            bank = bank_value;
        }

        bool resolve_symbols = false;
        if (arguments.contains("resolve_symbols") && arguments["resolve_symbols"].is_boolean())
            resolve_symbols = arguments["resolve_symbols"].get<bool>();

        bool detailed = false;
        if (arguments.contains("detailed") && arguments["detailed"].is_boolean())
            detailed = arguments["detailed"].get<bool>();

        std::vector<DisasmLine> lines = m_debugAdapter.GetDisassembly(start_address, end_address, bank, resolve_symbols);

        json result;

        if (detailed)
        {
            json instructions = json::array();

            for (const DisasmLine& line : lines)
            {
                json instr;
                std::ostringstream addr_ss, bank_ss, jump_ss;

                addr_ss << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << line.address;
                bank_ss << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << (int)line.bank;

                instr["address"] = addr_ss.str();
                instr["bank"] = bank_ss.str();
                instr["segment"] = line.segment;
                instr["instruction"] = line.name;
                instr["bytes"] = line.bytes;
                instr["size"] = line.size;

                if (line.jump)
                {
                    jump_ss << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << line.jump_address;
                    instr["jump_target"] = jump_ss.str();
                    instr["is_subroutine"] = line.subroutine;
                }

                if (line.irq > 0)
                {
                    instr["irq"] = line.irq;
                }

                instructions.push_back(instr);
            }

            result["instructions"] = instructions;
        }
        else
        {
            json instructions = json::array();

            for (const DisasmLine& line : lines)
            {
                std::ostringstream ss;
                ss << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << (int)line.bank;
                ss << ":" << std::setw(4) << line.address;
                ss << "  " << line.name;
                instructions.push_back(ss.str());
            }

            result["instructions"] = instructions;
        }

        result["count"] = lines.size();
        result["start_address"] = startAddrStr;
        result["end_address"] = endAddrStr;
        if (bank >= 0)
        {
            std::ostringstream bank_ss;
            bank_ss << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << bank;
            result["bank"] = bank_ss.str();
        }

        if (lines.empty())
        {
            result["note"] = "No disassembly records found. You may have asked for code that has not been executed yet. Code is only disassembled as it is executed.";
        }

        return result;
    }
    // Media info
    else if (normalizedTool == "get_media_info")
    {
        return m_debugAdapter.GetMediaInfo();
    }
    else if (normalizedTool == "list_recent_media")
    {
        return m_debugAdapter.ListRecentMedia();
    }
    // Chip status
    else if (normalizedTool == "get_z80_status")
    {
        return m_debugAdapter.GetZ80Status();
    }
    else if (normalizedTool == "get_vdp_registers")
    {
        return m_debugAdapter.GetVDPRegisters();
    }
    else if (normalizedTool == "get_vdp_status")
    {
        return m_debugAdapter.GetVDPStatus();
    }
    else if (normalizedTool == "get_psg_status")
    {
        return m_debugAdapter.GetPSGStatus();
    }
    else if (normalizedTool == "get_ay8910_status")
    {
        return m_debugAdapter.GetAY8910Status();
    }
    else if (normalizedTool == "get_screenshot")
    {
        return m_debugAdapter.GetScreenshot();
    }
    // Media and state management
    else if (normalizedTool == "load_media")
    {
        return {{"error", "load_media must be handled by the MCP manager"}};
    }
    else if (normalizedTool == "load_symbols")
    {
        std::string file_path = arguments["file_path"];
        return m_debugAdapter.LoadSymbols(file_path);
    }
    else if (normalizedTool == "list_save_state_slots")
    {
        return m_debugAdapter.ListSaveStateSlots();
    }
    else if (normalizedTool == "select_save_state_slot")
    {
        int slot = arguments["slot"];
        return m_debugAdapter.SelectSaveStateSlot(slot);
    }
    else if (normalizedTool == "save_state")
    {
        return m_debugAdapter.SaveState();
    }
    else if (normalizedTool == "load_state")
    {
        return m_debugAdapter.LoadState();
    }
    else if (normalizedTool == "set_fast_forward_speed")
    {
        int speed = arguments["speed"];
        return m_debugAdapter.SetFastForwardSpeed(speed);
    }
    else if (normalizedTool == "toggle_fast_forward")
    {
        bool enabled = arguments["enabled"];
        return m_debugAdapter.ToggleFastForward(enabled);
    }
    else if (normalizedTool == "get_rewind_status")
    {
        return m_debugAdapter.GetRewindStatus();
    }
    else if (normalizedTool == "rewind_seek")
    {
        int snapshot = arguments["snapshot"];
        return m_debugAdapter.RewindSeek(snapshot);
    }
    else if (normalizedTool == "controller_button")
    {
        int player = arguments["player"];
        std::string button = arguments["button"];
        std::string action = arguments["action"];
        return m_debugAdapter.ControllerButton(player, button, action);
    }
    else if (normalizedTool == "controller_macro")
    {
        return {{"error", "controller_macro must be handled by the MCP manager"}};
    }
    else if (normalizedTool == "list_sprites")
    {
        return m_debugAdapter.ListSprites();
    }
    else if (normalizedTool == "get_sprite_image")
    {
        int sprite_index = arguments.value("sprite_index", 0);
        return m_debugAdapter.GetSpriteImage(sprite_index);
    }
    // Disassembler operations
    else if (normalizedTool == "debug_run_to_cursor")
    {
        std::string addrStr = arguments["address"];
        u16 address;
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};
        return m_debugAdapter.RunToAddress(address);
    }
    else if (normalizedTool == "add_disassembler_bookmark")
    {
        std::string addrStr = arguments["address"];
        u16 address;
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};
        std::string name = arguments.value("name", "");
        return m_debugAdapter.AddDisassemblerBookmark(address, name);
    }
    else if (normalizedTool == "remove_disassembler_bookmark")
    {
        std::string addrStr = arguments["address"];
        u16 address;
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};
        return m_debugAdapter.RemoveDisassemblerBookmark(address);
    }
    else if (normalizedTool == "add_symbol")
    {
        std::string bankStr = arguments["bank"];
        std::string addrStr = arguments["address"];
        std::string name = arguments["name"];
        u8 bank;
        u16 address;
        if (!parse_mcp_hex_with_prefix(bankStr, &bank))
            return {{"error", "Invalid bank format"}};
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};
        return m_debugAdapter.AddSymbol(bank, address, name);
    }
    else if (normalizedTool == "remove_symbol")
    {
        std::string bankStr = arguments["bank"];
        std::string addrStr = arguments["address"];
        u8 bank;
        u16 address;
        if (!parse_mcp_hex_with_prefix(bankStr, &bank))
            return {{"error", "Invalid bank format"}};
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};
        return m_debugAdapter.RemoveSymbol(bank, address);
    }
    // Memory editor operations
    else if (normalizedTool == "select_memory_range")
    {
        int editor = arguments["area"];
        std::string startStr = arguments["start_address"];
        std::string endStr = arguments["end_address"];
        u32 start_address, end_address;
        if (!parse_mcp_hex_with_prefix(startStr, &start_address))
            return {{"error", "Invalid start_address format"}};
        if (!parse_mcp_hex_with_prefix(endStr, &end_address))
            return {{"error", "Invalid end_address format"}};
        return m_debugAdapter.SelectMemoryRange(editor, start_address, end_address);
    }
    else if (normalizedTool == "set_memory_selection_value")
    {
        int editor = arguments["area"];
        std::string valueStr = arguments["value"];
        u8 value;
        if (!parse_mcp_hex_with_prefix(valueStr, &value))
            return {{"error", "Invalid value format"}};
        return m_debugAdapter.SetMemorySelectionValue(editor, value);
    }
    else if (normalizedTool == "add_memory_bookmark")
    {
        int editor = arguments["area"];
        std::string addrStr = arguments["address"];
        std::string name = arguments.value("name", "");
        u32 address;
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};
        return m_debugAdapter.AddMemoryBookmark(editor, address, name);
    }
    else if (normalizedTool == "remove_memory_bookmark")
    {
        int editor = arguments["area"];
        std::string addrStr = arguments["address"];
        u32 address;
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};
        return m_debugAdapter.RemoveMemoryBookmark(editor, address);
    }
    else if (normalizedTool == "add_memory_watch")
    {
        int editor = arguments["area"];
        std::string addrStr = arguments["address"];
        std::string notes = arguments.value("notes", "");
        int size = arguments.value("size", 8);
        u32 address;
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};
        return m_debugAdapter.AddMemoryWatch(editor, address, notes, size);
    }
    else if (normalizedTool == "remove_memory_watch")
    {
        int editor = arguments["area"];
        std::string addrStr = arguments["address"];
        u32 address;
        if (!parse_mcp_hex_with_prefix(addrStr, &address))
            return {{"error", "Invalid address format"}};
        return m_debugAdapter.RemoveMemoryWatch(editor, address);
    }
    else if (normalizedTool == "list_disassembler_bookmarks")
    {
        return m_debugAdapter.ListDisassemblerBookmarks();
    }
    else if (normalizedTool == "list_symbols")
    {
        return m_debugAdapter.ListSymbols();
    }
    else if (normalizedTool == "get_call_stack")
    {
        return m_debugAdapter.ListCallStack();
    }
    else if (normalizedTool == "list_memory_bookmarks")
    {
        int area = arguments["area"];
        return m_debugAdapter.ListMemoryBookmarks(area);
    }
    else if (normalizedTool == "list_memory_watches")
    {
        int area = arguments["area"];
        return m_debugAdapter.ListMemoryWatches(area);
    }
    else if (normalizedTool == "get_memory_selection")
    {
        int area = arguments["area"];
        return m_debugAdapter.GetMemorySelection(area);
    }
    else if (normalizedTool == "memory_search_capture")
    {
        int area = arguments["area"];
        return m_debugAdapter.MemorySearchCapture(area);
    }
    else if (normalizedTool == "memory_search")
    {
        int area = arguments["area"];
        std::string op = arguments["operator"];
        std::string compare_type = arguments["compare_type"];
        int compare_value = arguments.value("compare_value", 0);
        std::string data_type = arguments.value("data_type", "unsigned");
        return m_debugAdapter.MemorySearch(area, op, compare_type, compare_value, data_type);
    }
    else if (normalizedTool == "memory_find_bytes")
    {
        if (!arguments.contains("area") || !arguments["area"].is_number_integer())
            return {{"error", "area is required"}};
        if (!arguments.contains("hex_bytes") || !arguments["hex_bytes"].is_string())
            return {{"error", "hex_bytes is required"}};

        int area = arguments["area"].get<int>();
        std::string hex_bytes = arguments["hex_bytes"].get<std::string>();
        return m_debugAdapter.MemoryFindBytes(area, hex_bytes);
    }
    else if (normalizedTool == "get_trace_log")
    {
        int start = arguments.value("start", 0);
        int count = arguments.value("count", 100);
        return m_debugAdapter.GetTraceLog(start, count);
    }
    else if (normalizedTool == "set_trace_log")
    {
        bool enabled = arguments["enabled"];
        u32 flags = arguments.value("flags", 0xFF);
        return m_debugAdapter.SetTraceLog(enabled, flags);
    }
    else
    {
        return {{"error", "Unknown tool: " + toolName}};
    }
}

void McpServer::SendResponse(const json& response)
{
    std::string line = response.dump(-1, ' ', false, json::error_handler_t::replace);
    m_transport->send(line);
}

void McpServer::SendError(int64_t id, int code, const std::string& message, const json& data)
{
    json error;
    error["jsonrpc"] = "2.0";
    error["id"] = id;
    error["error"] = {
        {"code", code},
        {"message", message}
    };

    if (!data.empty() && !data.is_null())
    {
        error["error"]["data"] = data;
    }

    Log("[MCP] Sending error: %s", error.dump().c_str());

    SendResponse(error);
}

void McpServer::LoadResources()
{
    char exe_path[1024];
    get_executable_path(exe_path, sizeof(exe_path));

    if (exe_path[0] == '\0')
        return;

    std::string base_path = exe_path;
    std::string resourcesPath = base_path + "/mcp/resources";

    if (!file_exists(resourcesPath + "/hardware/toc.json"))
        resourcesPath = base_path + "/../shared/desktop/mcp/resources";

    LoadResourcesFromCategory("hardware", resourcesPath + "/hardware/toc.json");
}

void McpServer::LoadResourcesFromCategory(const std::string& category, const std::string& tocPath)
{
    // Read toc.json file
    std::ifstream file(tocPath);
    if (!file.is_open())
    {
        // Resources directory not found, silently skip
        Log("[MCP] Warning: Resources TOC file not found: %s", tocPath.c_str());
        return;
    }

    std::stringstream buffer;
    buffer << file.rdbuf();
    std::string content = buffer.str();
    file.close();

    if (!json::accept(content))
    {
        Log("[MCP] Warning: Invalid JSON in resources TOC file: %s", tocPath.c_str());
        return;
    }

    json toc = json::parse(content);
    
    if (!toc.contains("toc") || !toc["toc"].is_array())
    {
        Log("[MCP] Warning: Invalid TOC format in resources TOC file: %s", tocPath.c_str());
        return;
    }

    // Get the directory containing toc.json
    std::string tocDir = tocPath.substr(0, tocPath.find_last_of("/\\"));

    for (const json& item : toc["toc"])
    {
        if (!item.contains("uri") || !item.contains("title"))
            continue;

        ResourceInfo resource;
        resource.uri = "gearcoleco://" + category + "/" + item["uri"].get<std::string>();
        resource.title = item["title"].get<std::string>();
        resource.description = item.contains("description") ? item["description"].get<std::string>() : "";
        resource.mimeType = item.contains("mimeType") ? item["mimeType"].get<std::string>() : "text/plain";
        resource.category = category;
        resource.filePath = tocDir + "/" + item["uri"].get<std::string>() + ".md";

        m_resources.push_back(resource);
        m_resourceMap[resource.uri] = resource;
    }
}

std::string McpServer::ReadFileContents(const std::string& filePath)
{
    std::ifstream file(filePath);
    if (!file.is_open())
    {
        Log("[MCP] Warning: Failed to open resource file: %s", filePath.c_str());
        return "";
    }

    std::stringstream buffer;
    buffer << file.rdbuf();
    return buffer.str();
}

void McpServer::HandleResourcesList(const json& request)
{
    if (!request.contains("id"))
    {
        SendError(0, -32600, "Invalid Request: missing id");
        return;
    }

    int64_t id = request["id"];

    json resources = json::array();

    for (const ResourceInfo& resource : m_resources)
    {
        json resourceJson;
        resourceJson["uri"] = resource.uri;
        resourceJson["name"] = resource.title;
        resourceJson["title"] = resource.title;
        resourceJson["description"] = resource.description;
        resourceJson["mimeType"] = resource.mimeType;

        resources.push_back(resourceJson);
    }

    json response;
    response["jsonrpc"] = "2.0";
    response["id"] = id;
    response["result"] = {
        {"resources", resources}
    };

    SendResponse(response);
}

void McpServer::HandleResourcesRead(const json& request)
{
    if (!request.contains("id"))
    {
        SendError(0, -32600, "Invalid Request: missing id");
        return;
    }

    int64_t id = request["id"];

    if (!request.contains("params") || !request["params"].contains("uri"))
    {
        SendError(id, -32602, "Invalid params: missing uri");
        return;
    }

    std::string uri = request["params"]["uri"];

    // Find resource
    std::map<std::string, ResourceInfo>::const_iterator it = m_resourceMap.find(uri);
    if (it == m_resourceMap.end())
    {
        SendError(id, -32602, "Resource not found: " + uri);
        return;
    }

    const ResourceInfo& resource = it->second;
    std::string content = ReadFileContents(resource.filePath);

    if (content.empty())
    {
        SendError(id, -32602, "Failed to read resource file: " + resource.filePath);
        return;
    }

    json response;
    response["jsonrpc"] = "2.0";
    response["id"] = id;
    response["result"] = {
        {"contents", json::array({
            {
                {"uri", resource.uri},
                {"mimeType", resource.mimeType},
                {"text", content}
            }
        })}
    };

    SendResponse(response);
}

