"""Run an arbitrary MCP server module over stdio.

This is a tiny, version-agnostic launcher used by `great_docs._mcp_docs`
to introspect an MCP server *through the wire protocol* rather than by reaching
into the `mcp` library's internal handler registries (which change between
major releases). The launcher imports the target module, locates its server
instance, and runs it over stdio using whichever `run` shape the installed
`mcp` version exposes.

Usage:

    python -m great_docs._mcp_runner <module_path> [<server_var>]
"""

from __future__ import annotations

import asyncio
import importlib
import sys
from typing import Any


def _find_server(module: Any, server_var: str | None) -> Any:
    """Locate the MCP `Server`/`FastMCP` instance in a module."""
    if server_var:
        return getattr(module, server_var, None)

    for attr_name in dir(module):
        obj = getattr(module, attr_name)
        type_name = type(obj).__name__
        module_name = type(obj).__module__ or ""
        if "mcp" in module_name and type_name in ("Server", "FastMCP"):
            return obj
    return None


async def _run(module_path: str, server_var: str | None) -> None:
    module = importlib.import_module(module_path)
    server = _find_server(module, server_var)
    if server is None:
        raise SystemExit(f"No MCP server instance found in {module_path}")

    # FastMCP knows how to run itself over stdio.
    if hasattr(server, "run_stdio_async"):
        await server.run_stdio_async()
        return

    # FastMCP exposes the underlying low-level server as `_mcp_server`.
    low = getattr(server, "_mcp_server", server)

    from mcp.server.stdio import stdio_server

    async with stdio_server() as (read_stream, write_stream):
        # mcp v1 requires initialization options; v2's `run` takes only the
        # streams. Try the v1 shape first and fall back on signature mismatch.
        init_options = None
        if hasattr(low, "create_initialization_options"):
            try:
                init_options = low.create_initialization_options()
            except Exception:
                init_options = None

        if init_options is not None:
            try:
                await low.run(read_stream, write_stream, init_options)
                return
            except TypeError:
                pass

        await low.run(read_stream, write_stream)


if __name__ == "__main__":
    module = sys.argv[1] if len(sys.argv) > 1 else ""
    var = sys.argv[2] if len(sys.argv) > 2 and sys.argv[2] else None
    if not module:
        raise SystemExit("usage: python -m great_docs._mcp_runner <module> [<var>]")
    asyncio.run(_run(module, var))
