手搓MCP,已经成功

用uv管理包

文章参考

https://blog.youkuaiyun.com/apo0625/article/details/147000978

安装uv

pip install uv

一)初始化client项目

进入自己的代码目录下,创建一个文件夹:

# 创建并初始化项目目录
uv init mcp_client

# 进入文件夹
cd mcp_client

# 创建虚拟环境
uv venv 

# 激活虚拟环境
# On Windows:
.venv\Scripts\activate
# On Unix or MacOS:
source .venv/bin/activate
# 安装 MCP依赖包
uv add mcp

后面,为了支持调用大模型,读取env环境变量中的API_KEY等信息,需要先安装如下依赖:

uv add mcp openai python-dotenv

2、编写MCP客户端
具体也可以参考官方示例:https://modelcontextprotocol.io/quickstart/client

客户端代码如下:

只是测试一下

import asyncio
import os
from openai import OpenAI
from dotenv import load_dotenv
from contextlib import AsyncExitStack

# 加载 .env 文件
load_dotenv()

class MCPClient:

    def __init__(self):
        """初始化 MCP 客户端"""
        self.exit_stack = AsyncExitStack()
        self.openai_api_key = os.getenv("API_KEY")  # 读取 OpenAI API Key
        self.base_url = os.getenv("BASE_URL")  # 读取 BASE URL
        self.model = os.getenv("MODEL")  # 读取 model

        if not self.openai_api_key:
            raise ValueError("未找到 API KEY. 请在.env文件中配置API_KEY")

        self.client = OpenAI(api_key=self.openai_api_key,
                             base_url=self.base_url)

    async def process_query(self, query: str) -> str:
        """调用 OpenAI API 处理用户问题"""
        messages = [{
            "role": "system",
            "content": "你是一个智能助手,帮助用户回答问题。"
        }, {
            "role": "user",
            "content": query
        }]

        try:
            # 调用 大模型API
            response = await asyncio.get_event_loop().run_in_executor(
                None,
                lambda: self.client.chat.completions.create(model=self.model,
                                                            messages=messages))
            return response.choices[0].message.content
        except Exception as e:
            return f"调用模型API时出错: {str(e)}"

    async def chat_loop(self):
        """运行交互式聊天循环"""
        print("MCP 客户端已启动!输入 'exit' 退出")

        while True:
            try:
                query = input("问: ").strip()
                if query.lower() == 'exit':
                    break

                response = await self.process_query(query)
                print(f"AI回复: {response}")

            except Exception as e:
                print(f"发生错误: {str(e)}")

    async def clean(self):
        """清理资源"""
        await self.exit_stack.aclose()


async def main():
    client = MCPClient()
    try:
        await client.chat_loop()
    finally:
        await client.clean()


if __name__ == "__main__":
    asyncio.run(main())

3、运行client
uv run client.py

除了配置远程api的模型

还可以用本地ollama

1、确保ollama已启动

没有启动的,输入如下命令来启动ollama服务

# 列出所有已安装模型
ollama start

# 运行大模型
ollama run qwq:latest

BASE_URL=http://localhost:11434/v1/
MODEL=qwq:latest
API_KEY=ollama

要创建和正确使用这个.env文件,请按照以下步骤操作:

1. ​​什么是.env文件?​

  • ​环境变量配置文件​​:用于存储应用程序的配置信息(API密钥、服务地址等)
  • ​安全性​​:将敏感信息与代码分离(不应提交到Git仓库)
  • ​格式​​:简单的键值对文本文件,每行一个变量 KEY=value

2. ​​创建.env文件的步骤​

方法一:手动创建(推荐)
  1. 在项目根目录新建名为 .env 的文本文件
  2. 粘贴以下内容(替换你的API_KEY为实际值):

BASE_URL=https://api.deepseek.com
MODEL=deepseek-chat
API_KEY="你的API_KEY"

# 安装依赖:pip install python-dotenv
from dotenv import load_dotenv
import os

# 加载 .env 文件
load_dotenv()  

# 读取配置
api_base = os.getenv("BASE_URL")
api_key = os.getenv("API_KEY")
model_name = os.getenv("MODEL")

回归正轨

四、MCP服务端
一)服务端代码
创建服务端文件 server.py:

from mcp.server.fastmcp import FastMCP

# 初始化FastMCP服务器
mcp = FastMCP("filesystem")

@mcp.tool()
async def create_file(file_name: str, content: str) -> str:
    """
    创建文件
    :param file_name: 文件名
    :param content: 文件内容
    """
    with open(file_name, "w", encoding="utf-8") as file:
        file.write(content)
        return "创建成功"

@mcp.tool()
async def read_file(file_name: str) -> str:
    """
    读取文件内容
    :param file_name: 文件名
    """
    with open(file_name, "r", encoding="utf-8") as file:
        return file.read()

@mcp.tool()
async def write_file(file_name: str, content: str) -> str:
    """
    写入文件内容
    :param file_name: 文件名
    :param content: 文件内容
    """
    with open(file_name, "w", encoding="utf-8") as file:
        file.write(content)
        return "写入成功"

if __name__ == "__main__":
    # 以标准 I/O 方式运行 MCP 服务器
    mcp.run(transport="stdio")

uv run server.py

 最后修改原来的代码
把client的py内容改成这样

import asyncio
import os
from openai import OpenAI
from dotenv import load_dotenv
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import json

# 加载 .env 文件
load_dotenv()


class MCPClient:

    def __init__(self):
        """初始化 MCP 客户端"""
        self.exit_stack = AsyncExitStack()
        self.api_key = os.getenv("API_KEY")  # 读取 OpenAI API Key
        self.base_url = os.getenv("BASE_URL")  # 读取 BASE URL
        self.model = os.getenv("MODEL")  # 读取 model

        if not self.api_key:
            raise ValueError("未找到 API KEY. 请在.env文件中配置API_KEY")

        self.client = OpenAI(api_key=self.api_key, base_url=self.base_url)

    async def process_query(self, query: str) -> str:
        """
        调用大模型处理用户查询并根据返回的tools列表调用对应工具
        """
        messages = [{"role": "user", "content": query}]

        response = await self.session.list_tools()

        available_tools = [{
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "input_schema": tool.inputSchema
            }
        } for tool in response.tools]
        print('服务端工具列表', available_tools)

        response = self.client.chat.completions.create(model=self.model,
                                                       messages=messages,
                                                       tools=available_tools)

        # 处理返回的内容
        content = response.choices[0]
        if content.finish_reason == "tool_calls":
            # 如何发现要使用工具,就执行工具
            tool_call = content.message.tool_calls[0]
            tool_name = tool_call.function.name
            tool_args = json.loads(tool_call.function.arguments)

            # 执行工具
            result = await self.session.call_tool(tool_name, tool_args)
            print(f"\n\n[Calling tool {tool_name} with args {tool_args}]\n\n")

            # 将模型返回的原始消息和工具执行的结果都添加到messages中
            messages.append(content.message.model_dump())
            messages.append({
                "role": "tool",
                "content": result.content[0].text,
                "tool_call_id": tool_call.id,
            })

            # 将上面的结果再返回给大模型生产最终的结果
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
            )
            return response.choices[0].message.content

        return content.message.content

    async def chat_loop(self):
        """运行交互式聊天循环"""
        print("MCP 客户端已启动!输入 'exit' 退出")

        while True:
            try:
                query = input("问: ").strip()
                if query.lower() == 'exit':
                    break

                response = await self.process_query(query)
                print(f"AI回复: {response}")

            except Exception as e:
                print(f"发生错误: {str(e)}")

    async def clean(self):
        """清理资源"""
        await self.exit_stack.aclose()

    async def connect_to_server(self, server_script_path: str):
        """
        连接到 MCP 服务器
        """
        is_python = server_script_path.endswith('.py')
        is_js = server_script_path.endswith('.js')
        if not (is_python or is_js):
            raise ValueError("不支持的文件类型")

        command = "python" if is_python else "node"
        server_params = StdioServerParameters(command=command,
                                              args=[server_script_path],
                                              env=None)
        # 启动 MCP 服务器并建立通信
        stdio_transport = await self.exit_stack.enter_async_context(
            stdio_client(server_params))
        self.stdio, self.write = stdio_transport
        self.session = await self.exit_stack.enter_async_context(
            ClientSession(self.stdio, self.write))

        await self.session.initialize()

    async def list_tools(self):
        """列出所有工具"""
        # 列出 MCP 服务器上的工具
        response = await self.session.list_tools()
        tools = response.tools
        print("已连接到服务器,server支持以下工具:", [tool.name for tool in tools])


async def main():

    # 启动并初始化 MCP 客户端
    client = MCPClient()
    try:
        # 连接到 MCP 服务器
        await client.connect_to_server('server.py')
        # 列出 MCP 服务器上的工具
        await client.list_tools()
        # 运行交互式聊天循环,处理用户对话
        await client.chat_loop()
    finally:
        # 清理资源
        await client.clean()


if __name__ == "__main__":
    asyncio.run(main())

 uv run client.py

启动客户端后,我们让AI通过MCP服务帮我们做一件事:在当前目录下aaa.txt文件内写入一句话:今天天气不错,心情很好!若文件不存在就创建

六、更多MCP服务器合集导航地址
号称最大的MCP集合导航站:https://mcp.so/

MCP集合:https://github.com/ahujasid/blender-mcp

官方MCP合集:https://github.com/modelcontextprotocol/servers

Github热门MCP导航:https://github.com/punkpeye/awesome-mcp-servers

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值