Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Xiaohei Companion

БесплатноНе проверен

MCP server that provides a tool to query the status of an electronic pet named Xiaohei, including mood, hunger level, and time since last played.

GitHubEmbed

Описание

MCP server that provides a tool to query the status of an electronic pet named Xiaohei, including mood, hunger level, and time since last played.

README

写在前面的话

本文由浅入深的介绍了 MCP 的相关内容,不同于技术栈或者工程化知识点的层层递进,本文只是简单介绍,具体应用环节还需要在实战过程中慢慢体会。

本文的代码地址:

时间线

我们先来对齐一下时间线:

时间 事件 说明
2022 年 10 月 ReAct 现世 论文ReAct:Synergizing Reasoning and Acting 发表,确立了推理 + 行动交替的 Agent 范式
2022 年末 ~ 2023 年中 Tool Use 现世 这一阶段的工具调用靠的是文本的正则解析:Langchain 的 ReAct 式 Agent 把 Action 或者 Action Input 拿出来,随后逐步向 「让 LLM 输出 JSON 迈进」
2023 年 6 月 13 日 OpenAI 协议的 Tool Use OpenAI 正式发布 Function Calling,把工具参数标准化为 JSON Schema
2024 年 11 月 25日 MCP 现世 Anthropic 发布 Model Context Protocol ,支持两种传输方式:Stdio & SSE + HTTP
2025 年 3 月 26 日 Streamable HTTP 协议 MCP 使用 Streamable 取代 SSE + HTTP
2025 年 10 月 16 日 Skill 现世 Anthropic 发布 Agent Skills

V1:单纯的Function Call

在 V1 中,我们先不涉及到任何 MCP,我们先看看 Function Call 的一个致命痛点。

我们先要定义一个工具,也就是写一个工具函数:

def get_xiaohei_status(detail: str = "all") -> dict:
    """模拟一个数据源:返回小黑此刻的状态。真实项目里这里会查数据库 / 调 API。"""
    full = {
        "name": "小黑",
        "mood": "有点想你",        # 心情
        "hunger": 72,             # 饥饿度 0-100,越高越饿
        "last_played_hours": 6,   # 距离上次陪它玩过去了几小时
    }
    if detail == "mood":
        return {"name": full["name"], "mood": full["mood"]}
    return full

那么,如果要让LLM 能够调用这个函数,需要给这个工具写好描述,也就是:

TOOLS_SCHEMA = [
    {
        "type": "function",
        "function": {
            "name": "get_xiaohei_status",
            "description": "查询电子宠物小黑此刻的状态,包括心情、饥饿度、距上次陪玩的时间。",
            "parameters": {
                "type": "object",
                "properties": {
                    "detail": {
                        "type": "string",
                        "enum": ["all", "mood"],
                        "description": "all=返回全部状态;mood=只返回心情",
                    }
                },
                "required": [],
            },
        },
    }
]

也就是以规定的格式来描述一下我们的 Tool,然后,把注册好的工具给到 LLM,让 LLM 知道工具的作用以及使用方法:

  response = client.chat.completions.create(
      model=MODEL,
      messages=messages,
      tools=TOOLS_SCHEMA,   # 关键:把 schema 交给 LLM
  )

最后用户Prompt进来之后,LLM判断要调用此工具,那么就在调用之后将工具返回的结果和用户Prompt一起给到LLM,得到最终回复。

这是一个成功完成Tool Use 也就是Function Call的过程,这其中有一个很致命的点:

如果别人要用这个工具,该怎么办?

我们工具函数和工具说明书都是自己来定的,注册方式也写到了代码中,完全无法拆开,这也就是真正的痛点,我们将在V2中解决它。

V2:Tool = MCP Sever

还是一样的工具函数,这里的一个关键点是我们在工具函数的前面添加了mcp的装饰器 @mcp.tool()

from mcp.server.fastmcp import FastMCP

# 给这个 Server 起个名字。Client 连上来 initialize 时会看到它。
mcp = FastMCP("xiaohei-companion")


@mcp.tool()
def get_xiaohei_status(detail: str = "all") -> dict:
    """查询电子宠物小黑此刻的状态,包括心情、饥饿度、距上次陪玩的时间。

    Args:
        detail: all=返回全部状态;mood=只返回心情。
    """
    # 注意:这段函数体和 v1 几乎一字不差——真正搬家的只是"它住在哪/怎么被找到"。
    # schema 不用手写了,FastMCP 会读上面的类型注解(detail: str)和 docstring 自动生成。
    full = {
        "name": "小黑",
        "mood": "有点想你",
        "hunger": 72,
        "last_played_hours": 6,
    }
    if detail == "mood":
        return {"name": full["name"], "mood": full["mood"]}
    return full

这个装饰器的作用有两个:

  1. 把函数的备注抄成description
  2. 添加 MCP 的类型判断

先说第一个,我们把这个函数的注释写在了函数头的下面,已经封装好的 MCP 会直接把这一段注释当成这个Tool 的 Description

至于第二个,被封装在了 FastMCP 内部,FastMCP 会自己来校验工具函数入参的类型,比如这里只支持字符串,那么如果在传入参数时,不是字符串,就会被拦在外面,不会进入到工具函数内容不。

我们可以通过下面的代码来获取到工具的相关信息:

session.initialize()

tools = await session.list_tools()
print("📋 Server 自报的工具列表(schema 是 Server 自动生成的):")
for t in tools.tools:
    print(f"  - {t.name}: {t.description}")
    print(f"    inputSchema: {t.inputSchema}")

获取到的信息如下所示:

📋 Server 自报的工具列表(schema 是 Server 自动生成的):
  - get_xiaohei_status: 查询电子宠物小黑此刻的状态,包括心情、饥饿度、距上次陪玩的时间。

Args:
    detail: all=返回全部状态;mood=只返回心情。

    inputSchema: {'properties': {'detail': {'default': 'all', 'title': 'Detail', 'type': 'string'}}, 'title': 'get_xiaohei_statusArguments', 'type': 'object'}

到这里,我们就解决了 V1 的痛点,工具函数 当然还是要自己来写,但是描述和格式校验可以有统一的标准了,我们要在 V3 中来详细聊聊调用的过程了。

V3:MCP Client + LLM

这里就进入到 MCP 的使用环节了,核心伪代码如下:

连接(Server):                          # ① 建立会话
    mcp_tools   = Server.list_tools()              # ② 问 Server 有哪些工具
    llm_tools   = [翻译(t) for t in mcp_tools]      # ③ MCP格式 → function calling 格式

    messages = [system, user问题]

    答复 = LLM(messages, tools=llm_tools)           # ④ 第一次问 LLM

    while 答复.要调工具:                             # ⑤ LLM 说"我要调工具"
        for 调用 in 答复.tool_calls:
            结果 = Server.call_tool(调用.名字, 调用.参数)   # ⑥ Client 替 LLM 去 Server 执行
            messages.append(结果)                         # ⑦ 把结果塞回对话
        答复 = LLM(messages, tools=llm_tools)        # ⑧ 再问一次 LLM

    return 答复.文字                                 # ⑨ 没有要调工具了 → 自然语言回复

这个过程无数人讲烂了已经,我就不再赘述了,我想讲几个点,首先是协议的问题

首先,MCP协议只有一种那就是 JavaScipt Object Notation - Remote Procedure Call ,简称为 JSON-RPC协议

它的核心很简单,就是想调用本地的函数一样,去调用另一台机器上的函数,人话 = 调用接口。

其次,MCP讨论比较高的是传输协议,我们在时间线表格里面梳理过了,MCP一共有三种协议,分别是:Stdio传输,SSE + HTTP 传输,Streamable + HTTP 传输。由于SSE + HTTP已经被废弃了,我们就不在多说,我们聊聊剩下两个。再开始之前,我们先规定好传输协议在哪一层,不然越聊越懵:

 --------------------------------------
| MCP 协议层:JSON-RPC 消息 						 | 
| initialize / list_tools / call_tool  | -> 永远不变
----------------------------------------
| 传输层,负责吧上面的 Json 从 A 搬到 B    | -> Stdio / Streamable + HTTP / ...
|--------------------------------------

Stdio 传输(Standard Input/Output )就像他的名字一样,传输方式是 Cline 当场讲他作为子进程拉起,也就是本地 MCP 的常见调用方式,既不需要端口,也不需要配置,不暴露到外侧。

Streamable + HTTP 的传输方式是,Server 端和 Client 端独立存在,Server 端可以被多个 Client 调用,虽然多了一层 HTTP 的开销,但是他可以解决一些场景下的难题,比如:

  • 一个工具要跑三十秒,Server 想中途推进度 -> 一直链接着,进度随时更新
  • Server 想反过来回调一下 Clinet -> 支持回调

from github.com/Nuo-chen-287/MCP-Basis

Установка Xiaohei Companion

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/Nuo-chen-287/MCP-Basis

FAQ

Xiaohei Companion MCP бесплатный?

Да, Xiaohei Companion MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Xiaohei Companion?

Нет, Xiaohei Companion работает без API-ключей и переменных окружения.

Xiaohei Companion — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Xiaohei Companion в Claude Desktop, Claude Code или Cursor?

Открой Xiaohei Companion на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Xiaohei Companion with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории development