ai-14-自己制作一个mcp服务helloworld(stdio方式)

What is the Model Context Protocol (MCP)? - Model Context Protocol

stdio(标准输入输出,本地子进程)

  • 工作方式:客户端(Cursor/Claude Desktop)把MCP服务作为本地子进程启动,通过进程的标准输入(stdin)、标准输出(stdout)收发JSON‑RPC报文。
  • 网络:不走TCP网络、没有端口,纯本机进程间通信。
  • 适用:本地运行的MCP服务(绝大多数日常开发场景)
  • 优点:最简单,零端口,不用处理HTTP、CORS;官方客户端原生支持。
  • 缺点:只能本机调用,不能给别的机器远程访问;不能多客户端共享同一个服务实例。
  • 配置示例:
"mcpServers": {
  "demo": {
    "command": "python",
    "args": ["server_stdio.py"]
  }
}

cherry studio 中配置

{
  "mcpServers": {
    "mcp-helloworld": {
      "command": "C:\\Users\\zg.cai\\Envs\\frida16-1-3\\Scripts\\python.exe",
      "args": [
        "D:\\czg\\czgAsFwgs\\Swkj\\frida-agent-example\\mcp‑helloworld\\server_STDIO.py"
      ],
      "env": {}
    }
  }
}

添加情况

mcp-helloworldp 添加到cherry studio


完整源码(已经跑通的版本)

#!/usr/bin/env python3
import asyncio
import json
from mcp.server.mcpserver import MCPServer

# 创建服务实例
mcp = MCPServer("mcp-helloworld-stdio")


@mcp.tool()
async def say_hello(username: str = "匿名用户") -> str:
    """输出一条 hello world 问候消息
    Args:
        username: 需要问候的用户名
    """
    return f"👋 Hello World! 你好,{username}。来自 python版 mcp‑helloworld MCP服务"


@mcp.tool()
async def get_server_info() -> str:
    """获取当前mcp‑helloworld服务信息"""
    info = {
        "serviceName": "mcp‑helloworld",
        "version": "1.0.0",
        "status": "running ok"
    }
    return json.dumps(info, ensure_ascii=False, indent=2)


if __name__ == "__main__":
    # stdio 模式,供 Cherry‑Studio 调用
    mcp.run(transport="stdio")

逐段深度讲解(mcp‑2.x)

1.导入模块

import asyncio
import json
from mcp.server.mcpserver import MCPServer
  • asyncio:Python异步库。MCP服务底层全部是异步IO。
  • json:用来序列化字典为JSON字符串(get_server_info返回)
  • MCPServer2.x新版高级封装类,就是以前1.x版本的FastMCP,改了名字。

MCPServer = 高级封装,帮你自动处理所有 JSON‑RPC 底层报文,不用手动写 tools/listtools/call 回调。

2.实例化MCP服务

mcp = MCPServer("mcp-helloworld")
  • "mcp‑helloworld":你的MCP服务名称标识,客户端(Cherry‑Studio)会看到这个名字。
  • mcp 对象就是你整个服务的管理器,所有工具都注册到它身上

3.第一个工具函数 say_hello

@mcp.tool()
async def say_hello(username: str = "匿名用户") -> str:
    """输出一条 hello world 问候消息
    Args:
        username: 需要问候的用户名
    """
    return f"👋 Hello World! 你好,{username}。来自 python版 mcp‑helloworld MCP服务"

@mcp.tool() 装饰器(最核心)

等价于告诉MCP框架:把下面这个函数注册成一个可供AI调用的工具
底层自动帮你完成三件大事:

  1. 生成工具名称:函数名 say_hello
  2. 生成工具描述:读取函数的文档字符串(三个引号里面的文字)
  3. 自动解析函数参数 username,自动生成工具的 input_schema JSON结构。

async

MCP框架要求所有工具函数必须是异步 async 函数

参数 username: str = "匿名用户"

  • username:对外暴露给AI的入参
  • str:参数类型
  • "匿名用户":默认值,AI不传这个参数的时候就用它

文档字符串规范

"""输出一条 hello world 问候消息
Args:
    username: 需要问候的用户名
"""
  • 第一行 = 工具的简短描述
  • Args区块 = 每个参数的说明
    MCPServer会自动解析这段文字,生成完整工具元数据,Cherry‑Studio/AI就能看懂每个参数干什么用。

return

返回字符串,就是工具执行完成后返回给大模型的结果。

4.第二个工具 get_server_info

@mcp.tool()
async def get_server_info() -> str:
    """获取当前mcp‑helloworld服务信息"""
    info = {
        "serviceName": "mcp‑helloworld",
        "version": "1.0.0",
        "status": "running ok"
    }
    return json.dumps(info, ensure_ascii=False, indent=2)
  • json.dumps:字典转漂亮格式化JSON字符串
  • ensure_ascii=False:支持中文不乱码
  • indent=2:换行缩进,输出美观

5.程序入口,启动服务

if __name__ == "__main__":
    mcp.run(transport="stdio")
  • if __name__ == "__main__":只有直接运行 python server.py 的时候才执行启动代码,被别的文件import导入时不会启动服务。
  • transport="stdio"传输模式,标准输入输出
    • MCP客户端(Cherry‑Studio)启动你的python进程
    • 客户端通过标准输入(stdin) 给你发JSON‑RPC请求(比如:列出工具、调用工具)
    • 你的Python程序通过标准输出(stdout) 返回结果给客户端

这就是为什么你在IDEA直接运行,控制台一片空白:stdio模式不会打印日志,所有通信流量都走后台stdin/stdout通道。

MCP 整体工作流程(客户端 ↔ 你的server.py)

  1. Cherry‑Studio启动 python server.py 子进程
  2. 客户端发送 tools/list 请求 → MCPServer自动扫描你所有@mcp.tool()装饰的函数,返回工具列表
  3. AI决定调用某个工具,客户端发送 tools/call 请求(携带工具名+参数)
  4. MCPServer框架自动路由,执行你对应的 async 函数
  5. 函数return结果 → MCPServer封装成JSON‑RPC响应发回Cherry‑Studio → 交给大模型

你以后新增工具,只需要照抄模板即可

@mcp.tool()
async def my_new_tool(param1: str) -> str:
    """这里写工具描述
    Args:
        param1: 参数说明
    """
    # 你的业务逻辑
    result = "xxx"
    return result

不需要修改启动代码、不需要手动注册回调。

容易踩坑的知识点总结

  1. 工具函数必须 async,不能写普通def同步函数
  2. 描述、参数说明必须写在函数文档字符串里,框架自动读取,不要写注释
  3. transport="stdio" 不要改成别的,Cherry‑Studio只支持stdio模式
  4. 不要在代码里随便print!print会污染stdout数据流,导致JSON‑RPC报文出错,MCP通信崩溃。

如果需要打印调试日志,要用 print("xxx", file=sys.stderr),输出到stderr(错误流),不会干扰通信。


运行&调试方式

方式1:直接命令行运行(直接运行不会看到交互,stdio等待父进程)

python server_STDIO.py

执行后屏幕空白,不是卡死!它在等待父进程发送MCP JSON‑RPC报文,不能直接手动输入文字

方式2:用 MCP‑Inspector 调试(推荐)

Inspector 充当父进程,fork这个python子进程:

npx @modelcontextprotocol/inspector@latest python server_STDIO.py

(frida16-1-3) PS D:\czg\czgAsFwgs\Swkj\frida-agent-example\mcp‑helloworld\mcp_STDIO方式> npx @modelcontextprotocol/inspector@latest python server_STDIO.py
Starting MCP inspector...
Sandbox: port 6275 in use; falling back to an OS-assigned port. The MCP Apps tab will work locally, but the sandbox is no longer on a predictable port — set MCP_SANDBOX_PORT to a free one if you need to forward it (container / SSH tunnel / reverse proxy).
App origin: port 6278 in use; falling back to an OS-assigned port. Apps declaring _meta.ui.domain will still render, but the origin they are served from is no longer predictable — set MCP_APP_ORIGIN_PORT to a free one if your app's backend allowlists it.
MCP Inspector PORT IS IN USE at http://127.0.0.1:6274
(frida16-1-3) PS D:\czg\czgAsFwgs\Swkj\frida-agent-example\mcp‑helloworld\mcp_STDIO方式>

浏览器打开 inspector UI,Transport自动是stdio,直接点Connect,即可看到两个工具,调用测试。

npx运行情况mcp调试工具.png

Cherry‑Studio / Cursor 配置(stdio模式)

{
  "mcpServers": {
    "mcp-helloworld-stdio": {
      "command": "C:\\Users\\zg.cai\\Envs\\frida16-1-3\\Scripts\\python.exe",
      "args": [
        "D:\\czg\\czgAsFwgs\\Swkj\\frida-agent-example\\mcp_helloworld\\mcp_STDIO方式\\server_STDIO.py"
      ],
      "env": {}
    }
  }
}

claude 客户端中配置


image.png
claude 配置mcp
claude 客户端中配置
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容