"""MCP server + client 最小可运行示例（无需任何 API key）。

演示 MCP 三方角色与三大 server 原语：
- Host：本进程 = 宿主 AI 应用（这里用一个精简的学习版 host loop 代替真实 LLM）
- Client：`mcp.Client`（SDK 自动完成连接协商）
- Server：`MCPServer`，暴露 tools / resources / prompts

运行（使用 uv，无需预装依赖；也可 `pip install mcp` 后用 python 直接跑）：
    uv run --with mcp python 01_server_primitives_demo.py

依赖：mcp Python SDK v2（截至 2026-08-30 当前稳定版线，实测 2.1.1），
来源: https://github.com/modelcontextprotocol/python-sdk（访问于 2026-08-30）
"""

from mcp import Client
from mcp.server import MCPServer

# ---------------------------------------------------------------------------
# 1. 定义一个 MCP server：两个 tool、一个 resource template、一个 prompt
#    —— 类型注解就是 JSON Schema，docstring 就是给模型看的工具描述
# ---------------------------------------------------------------------------

mcp = MCPServer("bookshop", instructions="Search the catalog before recommending a book.")


@mcp.tool(title="Search the catalog")
def search_books(query: str, limit: int = 5) -> str:
    """Search the catalog by title or author."""
    return f"Found 3 books matching {query!r} (limit={limit})."


@mcp.tool(title="Look up a book")
def lookup_book(title: str) -> dict:
    """Look up a book by its exact title."""
    return {"title": title, "author": "Frank Herbert", "year": 1965}


@mcp.resource("catalog://{section}")
def catalog_section(section: str) -> str:
    """Return the catalog listing for a section (sci-fi, poetry, ...)."""
    return f"catalog section {section!r}: Dune, Solaris, Left Hand of Darkness"


@mcp.prompt()
def recommend(book_topic: str) -> str:
    """Template: ask for a recommendation on a topic."""
    return f"Recommend one book about {book_topic} and explain why in one sentence."


# ---------------------------------------------------------------------------
# 2. Host 的职责之一：把 server 的工具发现结果「翻译」给模型。
#    这里我们不调用真实 LLM（无需 key），只展示 host 侧会拿到的数据结构。
# ---------------------------------------------------------------------------


async def main() -> None:
    # Client(mcp)：进程内 transport —— 与真实 stdio/HTTP 部署同一套协议语义
    async with Client(mcp) as client:
        print("== 连接协商（SDK 自动）==")
        print("server_info        :", client.server_info.name)
        print("protocol_version   :", client.protocol_version)
        caps = client.server_capabilities
        print("capabilities       : tools=%s resources=%s prompts=%s" % (
            "tools" in (caps.model_dump() or {}), None, None))
        print("server_instructions:", client.instructions)

        print("\n== tools/list（host 据此组装 Claude API 的 tools 参数）==")
        tools = await client.list_tools()
        for t in tools.tools:
            print(f"- {t.name}: {t.description} schema_required={t.input_schema.get('required')}")

        print("\n== tools/call（模型选中 search_books 后，host 执行）==")
        result = await client.call_tool("search_books", {"query": "dune"})
        for block in result.content:
            print("  content         :", getattr(block, "text", block))
        print("  structured      :", result.structured_content)
        print("  is_error        :", result.is_error)

        print("\n== resources/templates/list + resources/read（应用控制的数据面）==")
        templates = await client.list_resource_templates()
        for tpl in templates.resource_templates:
            print("  template        :", tpl.uri_template)
        r = await client.read_resource("catalog://sci-fi")
        for c in r.contents:
            print("  resource content:", getattr(c, "text", c))

        print("\n== prompts/list + prompts/get（用户控制的模板）==")
        prompts = await client.list_prompts()
        for p in prompts.prompts:
            print("  prompt          :", p.name, "| args:", [a.name for a in (p.arguments or [])])
        rendered = await client.get_prompt("recommend", {"book_topic": "deserts"})
        for m in rendered.messages:
            print("  rendered message:", getattr(m.content, "text", m.content))


if __name__ == "__main__":
    import asyncio

    asyncio.run(main())
