【MCP】小白详解从0开始构建MCP服务端全流程

简介

构建MCP服务端的目的是用于串通我的项目中各种结构和零件 MCP可以分离代码的llm的工作部分,大模型专注于它最擅长的"思考",而我们的代码专注于我们擅长的"执行"。这使得整个系统更加清晰和模块化。 为此,我将讲一下构建MCP的流程

MCP服务端的构建

然后对于mcp函数的内部 首先导入必要的包

python 复制代码
import os # 获取地址
import logging # 导出日志,可选
from mcp.server.fastmcp import FastMCP # 导入MCP服务器框架

初始化服务器

ini 复制代码
mcp = FastMCP("file-reader")

定义一个工具,这个工具就是llm会调用的工具列表,是整个服务器最重要的部分 @mcp.tool() 是一个"装饰器" 它告诉 FastMCP 框架:下面这个普通的 Python 函数是一个工具 请把它注册并暴露给外部。、 这里的函数内部可以是任何想拥有的内容,这里只展示读取其他文件的效果

python 复制代码
@mcp.tool()
async def read_file(filename: str) -> str:
"""
    简要分析

    Args:(示例)
        filename:参数含义
        username: 参数含义。
    
    Returns:
        一个包含分析结果的JSON字符串。(返回示例。自行修改)
    """

后面必须跟一段函数解释 这一段的解释是为了之后返回给大语言模型的描述 这是对这个函数功能的自然语言描述。这是整个结构中对大模型来说最重要的一部分! 大模型就是通过阅读这段描述来理解"这个工具是干什么用的?"、"我应该在什么时候用它?"。一个清晰、准确的描述,是让大模型能正确使用工具的关键 可以在下列自行添加分析后的效果: 例如 最后写一段MCP持续被监听的函数,这时候MCP就会通过 通过标准输入/输出(stdio)来监听来自客户端的连接和指令。

python 复制代码
if __name__ == "__main__":
    logging.info("--- MCP 文件读取服务器启动 ---")
    mcp.run(transport='stdio')

主代码群的初始化MCP

首先需要启动mcp服务,这里是构建两个关键的参数,一个是session,一个是tool 这里由于是直接启动以py文件构建的服务端,所以我们首先要获取该文件的相对路径,然后配置python解释器

python 复制代码
mcp_server_path = os.path.join(current_dir, "CausalChatMCP", "mcp_server.py")
server_params = StdioServerParameters(command=sys.executable, args=[mcp_server_path])

获取完之后,我们就需要我们就要运行该程序,并建立起主程序和子程序之间的通信 stdio_client(server_params) 启动 mcp_server.py 子进程, 由于mcp需要输入输出流,所以在这里定义两个参数 注意,这里的enter_async_context指的是使用异步上下文管理器管理子进程生命周期 当退出上下文时自动终止子进程,这里的mcp_process_stack一般是异步上下栈的实例

python 复制代码
read_stream, write_stream = await mcp_process_stack.enter_async_context(stdio_client(server_params))

建立完通信之后,并没有载体进行互连,所以建立一个session进行手法MCP的消息

python 复制代码
session = await exit_stack.enter_async_context(ClientSession(read_stream, write_stream))
await session.initialize()

建立完通信之后,就需要获取我们在MCP中定义的工具,工具有哪些呢 获取完工具之后,我们需要转换为LLm所需要的格式信息 其中的MCPtool是严格定义的

  1. "type": "function": 含义: 这是在告诉大模型,我提供给你的这个工具是一个函数(function),你可以像调用函数一样调用它。 是否可变: 不可变。这是API定义好的类型。
  2. "function": 含义: 这是一个JSON对象,用来包裹这个函数的所有具体信息。 是否可变: 不可变。这是固定的结构。
  3. "name": tool.name: 含义: 这是函数的名字。这个名字非常重要,它就是这个工具的唯一ID。当大模型决定要调用这个工具时,它就会通过这个名字来告诉我们。 tool.name 的值来自于哪里?来自于您在 py 中定义的函数名
  4. "description": tool.description: 含义: 这是对这个函数功能的自然语言描述。这是整个结构中对大模型来说最重要的一部分! 大模型就是通过阅读这段描述来理解"这个工具是干什么用的?"、"我应该在什么时候用它?"。一个清晰、准确的描述,是让大模型能正确使用工具的关键。 tool.description 的值来自于哪里?来自于在 py 中为函数编写的文档字符串。
  5. "parameters": tool.inputSchema: 含义: 这里定义了函数需要哪些参数,以及这些参数的类型和格式。它使用的是一种叫做"JSON Schema"的规范。这等于告诉大模型:"如果你要调用read_file,你必须提供一个名为filename的参数,并且它必须是一个字符串。" tool.inputSchema 的值来自于哪里?来自于 mcp 框架对 参数类型注解的自动解析,例如 filename: str。

那么对于mcp自动回应的参数就是如下格式

python 复制代码
{
  "type": "function",
  "function": {
    "name": "xx",
    "description": "xx",
    "inputScheme": {
        "username": {
          "type": "string"
        },
        "filename": {
          "type": "string"
        }
      },
      "required": ["username"]
    }
}

我们获取的mcp_tool结构就如下:

python 复制代码
tools_response = await session.list_tools()
        mcp_tools = [{
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "parameters": tool.inputSchema,
            }
        } for tool in tools_response.tools]

该部分的完整代码如下

python 复制代码
 current_dir = os.path.dirname(os.path.abspath(__file__))
        mcp_server_path = os.path.join(current_dir, "CausalChatMCP", "mcp_server.py")
        
        server_params = StdioServerParameters(command=sys.executable, args=[mcp_server_path])
        
        read_stream, write_stream = await mcp_process_stack.enter_async_context(stdio_client(server_params))
        session = await mcp_process_stack.enter_async_context(ClientSession(read_stream, write_stream))
        await session.initialize()
        
        tools_response = await session.list_tools()
        mcp_tools = [{
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "parameters": tool.inputSchema,
            }
        } for tool in tools_response.tools]

        mcp_session = session
        logging.info(f"MCP服务器连接成功,会话已激活。发现工具: {[tool['function']['name'] for tool in mcp_tools]}")
        

主代码群的调用MCP

当我们在主程序初始化好了MCP所需要的输入输出流,通信工具之后,我们就需要让LLm调用工具了 那么在openai的标准化定义之中,我们将MCPtool传入response之中,又LLM来决定是否调用MCP

python 复制代码
response = client.chat.completions.create(
        model=current_model,
        messages=messages,
        tools=mcp_tools or None,
        tool_choice="auto",
    )
response_message = response.choices[0].message
tool_calls = response_message.tool_calls

这里的tool_calls是市面上的大模型所定义的,所以可以返回我调用了什么工具,或者没有调用 这里返回的结果需要注意 这里的response_message并没有返回一个包含工具的结果,他只返回了一次调用工具的指令 比如

  1. LLM 理解了用户的意图。
  2. 它查看了工具清单,发现 read_file 工具正好能满足这个意图。
  3. 它决定:"好的,我需要调用 read_file,然后就返回给用户了

所以获取到指令之后,需要在主程序中调用他,首先获取工具所调用的名称和参数 首先获取函数名和函数参数,返回给MCP的name和args 用function_response_obj获取返回给MCP返回的响应列表

python 复制代码
 {
      "status": "success",
      "content": [  // 内容可以是一个列表,允许多种类型的返回
        {
          "type": "text",
          "text": "。。。"
        }
      ],
      "metadata": { "execution_time": "0.01s" }
    }

通过function_response_tex来获取我们要的东西

python 复制代码
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
function_response_obj = await mcp_session.call_tool(function_name, function_args)
function_response_text = function_response_obj.content[0].text

调用完我们所需要的东西之后,我们就可以第二次输入给llm总结,加入到历史消息之中

python 复制代码
messages.append({
            "tool_call_id": tool_call.id, # 这里id需要对应第一次调用的id
            "role": "tool",
            "name": function_name,
            "content": function_response_text,
        })
second_response = client.chat.completions.create(
     model=current_model,
     messages=messages,
 )
 summary_text = second_response.choices[0].message.content

这里输出的就是llm所调用的东西了,也就是整个流程需要调用两次。

相关推荐
人生都在赌8 小时前
MCP最佳实践与性能优化:构建高效稳定的AI工具连接器
ai编程·cursor·mcp
岳嵩Ben13 小时前
用 MCP Prompts 构建 AI 驱动的项目文档管理工具:SoloFlow MCP
mcp
寅时码1 天前
消除大模型幻觉,让AI-IDE真正理解代码,打通LSP与AI的任督二脉
visual studio code·cursor·mcp
SugarPPig2 天前
使用的IDE没有内置MCP客户端怎么办?
ide·mcp
ffutop2 天前
MCP 能力探索
mcp
带刺的坐椅2 天前
Solon v3.4.2(Java 应用开发生态基座)
java·ai·solon·liteflow·mcp
青衫客362 天前
LLM—— 基于 MCP 协议(Stdio 模式)的工具调用实践
大模型·llm·mcp
友莘居士3 天前
本地使用postman调试mcp接口
测试工具·postman·sse·mcp
摘星编程3 天前
MCP提示词工程:上下文注入的艺术与科学
人工智能·提示词工程·a/b测试·mcp·上下文注入
思绪漂移3 天前
阿里云 【免费试用】MCP 赋能可视化 OLAP 智能体应用
阿里云·云计算·agent·云原生数据库·mcp