python fastmcp mcp框架 、ollama构建AI Agent
1、定义工具目标和背景:明确AI Agent要解决的问题和应用场景。
2、构建核心功能:包括数据加载、向量化、本地存储和语义检索。
3、本地测试:确保AI Agent的功能正常。
4、构建MCP服务器:使用FastMCP框架创建MCP服务器。
5、配置AI Agent项目:在工具内部运行AI Agent。
首先要了解为什么有这样类似所谓的mcp,他是Model Context Protocol (MCP) 的缩写,我们经常被这些的概念搞晕,举个实际例子我们就明白了
一般我们用的大数据模型是训练好的,,被训练的数据是死的,所以叫预训练模型,当然我们也可以更改数据包括添加新的数据,但是几乎没有什么时效性
如果我们想了解明天的天气,大模型原则上是不会给你正确的答案,因为这些数据是未来的,而且是有时效性的,那么我们做个工具,提供给大模型知道,我这里能查到未来的天气,也可能就是一个函数,所以现在就有了mcp server ,他负责获取未来的天气数据,然后提供给客户端mcp client ,客户端会集成ollama以及相关访问大模型的api接口,当然这些大模型接口自己搭建的可能会是费用不高,但是算力可能不足,毕竟大模型要跑的快还能精准,硬件配置是要上去的,假如你连接到商业的大模型提供商,并且注册了大模型api和密钥,实际这种费用还是很高的,毕竟token是用频率会很高,我们回过来再说刚才的话题,有以下几点:
1.client里面会有访问 mcp server 暴露的天气的函数
2.访问大模型的接口(比如:api地址,api密钥等信息),本地的或中小规模就可以使用ollama大模型部署连接自己搭建的大模型系统
3. 你会在客户端应用里面会加上一段pormpt,通知大模型,用户一旦要访问实时天气,请调用mcp server 的天气函数
4. ai对话的程序,一个普通客户连接上ai对话,发起对话,会把需要问的内容,传递给client程序,程序会调用大模型接口,
大模型会智能的判断,有一个新工具即天气函数可以解决用户的这个问题,他就会调用mcp server的天气函数,而不是胡乱给出逻辑推理。
注意一点: 如果本地测试有的大模型是不支持tools,也就是mcp协议的。
下图就展示了基本的访问流程,展示了典型的MCP客户端与服务器交换。客户端首先请求服务器能力信息。客户端随后发送命令或查询,服务器处理这些请求并相应响应。
这使得AI代理与外部工具之间实现无缝交互。(这里mcp client 会给用户提供一个AI界面,询问用户需求,客户端程序会利用大模型,大模型根据判断是否要调用mcp server)
场景需求
1. 客户端的desktop软件,需要AI增强功能,则可以把mcp client和mcp server 集成到一起,mcp server会被制作成插件或本地程序,访问外部功能,而本地的client调用本地的server
这个当中,client是集成了访问大模型的ai功能的
2.mcp client和mcp server 很多情况下也是分离的,同样是桌面软件的插件可以通过配置mcp server ,直接通过ai调用mcp server
当然场景还很多,包括读取放在不同地点的数据。包括本地内部保密数据等。
一、fastmcp
fastmcp 是python的一个mcp 框架,来实现 mcp server和mcp client。
https://github.com/jlowin/fastmcp (官方源码地址)
Welcome to FastMCP 2.0! - FastMCP
安装 1.需要3.10以上的python解析器支持 2.他需要依赖mcp库的支持,可以通过pip install mcp 安装 如果你不安装fastmcp,但是单独使用mcp库,则需要pip install mcp进行单独安装 否则你不需要单独下载,fast-mcp安装时会自动下载 3.fastmcp包依赖信息
.(.venv) PS C:\Users\king\PycharmProjects\PythonProjectmcp310up\fastmcp2> pip show fastmcp Name: fastmcp Version: 2.13.2 Summary: The fast, Pythonic way to build MCP servers and clients. Home-page: https://gofastmcp.com Author: Jeremiah Lowin Author-email: License-Expression: Apache-2.0 Location: C:\Users\king\PycharmProjects\PythonProjectmcp310up\.venv\Lib\site-packages Requires: authlib, cyclopts, exceptiongroup, httpx, jsonschema-path, mcp, openapi-pydantic, platformdirs, py-key-value-aio, pydantic, pyperclip, python-dotenv, rich, uvicorn, websockets Required-by:
4.查看mcp官网
(.venv) PS C:\Users\king\PycharmProjects\PythonProjectmcp310up\fastmcp2> pip show mcp
Name: mcp
Version: 1.12.1
Summary: Model Context Protocol SDK
Home-page: https://modelcontextprotocol.io
Author: Anthropic, PBC.
Author-email:
License: MIT
Location: C:\Users\king\PycharmProjects\PythonProjectmcp310up\.venv\Lib\site-packages
Requires: anyio, httpx, httpx-sse, jsonschema, pydantic, pydantic-settings, python-multipart, pywin32, sse-starlette, starlette, uvicorn
Required-by: fast-agent-mcp, fastmcp
查看下版本信息
(.venv) PS C:\Users\king\PycharmProjects\PythonProjectmcp310up\fastmcp2> fastmcp version
FastMCP version: 2.13.2
MCP version: 1.21.1
Python version: 3.12.10
Platform: Windows-11-10.0.26100-SP0
FastMCP root path: C:\Users\king\PycharmProjects\PythonProjectmcp310up\.venv\Lib\site-packages
二、 简单实例
1、先构建simpleserver.py
from fastmcp import FastMCP mcp = FastMCP("myServer") #fastMCP 支持广泛的类型注解,包括所有 Pydantic 类型: #创建工具1 @mcp.tool() def output(name: str) -> str: print("client端正在调用output--------------") return f"obj1:{name}" @mcp.tool def add(fp1: int, fp2: int) -> int: """将两个整数相加。""" print("client端正在调用add--------------") return fp1 + fp2 if __name__ == "__main__": mcp.run()
2. client.py
import asyncio from fastmcp import Client """ 注意: 1.客户端和服务器端的字段名称要一致 2.传入的数据类型为dict类型 """ #服务器端口 8000 client = Client("http://localhost:8000/mcp") async def call_tool(name: str): async with client: result = await client.call_tool("output", {"name": name}) print(result) async def call_tool_add(a: int, b: int): async with client: #fp1,fp1为server.py add函数定义的形参名称 result = await client.call_tool("add", {"fp1":a, "fp2":b }) print(result) asyncio.run(call_tool("aozhejin")) asyncio.run(call_tool_add(1,2)) """ CallToolResult(content=[TextContent(type='text', text='obj1:Ford', annotations=None, meta=None)], structured_content={'result': 'obj1:Ford'}, meta=None, data='obj1:Ford', is_error=False)
3.命令行启动(介绍三种方式)
你可以看下源码 fastmcp/server/server.py
##########################################################
... if transport not in {"stdio", "http", "sse", "streamable-http"}:
############################################################
当然如果你想启动 mcp inspector 请用 fastmcp dev <server.py>
1) 命令行方式(一)
sse方式启动
C:\Users\king\PycharmProjects\PythonProjectmcp310up\fastmcp2> fastmcp run simpleserver.py --transport sse --port 8000 ╭──────────────────────────────────────────────────────────────────────────────╮ │ │ │ ▄▀▀ ▄▀█ █▀▀ ▀█▀ █▀▄▀█ █▀▀ █▀█ │ │ █▀ █▀█ ▄▄█ █ █ ▀ █ █▄▄ █▀▀ │ │ │ │ FastMCP 2.13.2 │ │ │ │ │ │ 🖥 Server name: myServer │ │ │ │ 📦 Transport: SSE │ │ 🔗 Server URL: http://127.0.0.1:8000/sse │ │ │ │ 📚 Docs: https://gofastmcp.com │ │ 🚀 Hosting: https://fastmcp.cloud │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ [12/03/25 17:55:04] INFO Starting MCP server 'myServer' with transport 'sse' on http://127.0.0.1:8000/sse server.py:2058 INFO: Started server process [19408] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
客户端访问
程序内:client = Client("http://localhost:8000/sse")
2) 命令行方式(二)
http方式连接
(.venv) PS C:\Users\king\PycharmProjects\PythonProjectmcp310up\fastmcp2> fastmcp run server3.py:mcp --transport http --port 8000
启动界面如下
╭──────────────────────────────────────────────────────────────────────────────╮ │ │ │ ▄▀▀ ▄▀█ █▀▀ ▀█▀ █▀▄▀█ █▀▀ █▀█ │ │ █▀ █▀█ ▄▄█ █ █ ▀ █ █▄▄ █▀▀ │ │ │ │ FastMCP 2.13.2 │ │ │ │ │ │ 🖥 Server name: StrictServer │ │ │ │ 📦 Transport: HTTP │ │ 🔗 Server URL: http://127.0.0.1:8000/mcp │ │ │ │ 📚 Docs: https://gofastmcp.com │ │ 🚀 Hosting: https://fastmcp.cloud │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ [12/03/25 17:20:09] INFO Starting MCP server 'StrictServer' with transport 'http' on http://127.0.0.1:8000/mcp server.py:2058 INFO: Started server process [62100] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
客户端client.py
程序内代码: client = Client("http://localhost:8000/mcp")
我们也可以看到用的是Uvicorn ASGI web服务器(异步服务器),他嵌入的方式类似sprintboot内部嵌入jetty.很方便作为一个服务器使用。
3)命令行方式(三)
stdio方式启动,可以参考下面的
4.利用mcp.run()
1) mcp.run("stdio")/mcp.run() 两种方式都能以stdio方式提供服务
客户端要以server的文件名字来进行访问,他会直接访问本地simpleserver.py中定义的函数,
但是要注意(要么设置为mcp.run(),要么设置为mcp.run(transport="stdio"),设置为其他值,你得不到想要的结果)
simpleserver.py from fastmcp import FastMCP mcp = FastMCP("myServer")import sys @mcp.tool def add(fp1: int, fp2: int=5) -> int: return fp1 + fp2
#在pycharm项目中直接右键点运行,就可以访问simpleserer.py提供的服务。
testclient.pyimport asyncio from fastmcp import Client from fastmcp.client.transports import StdioTransport transport = StdioTransport( command="python", args=["simpleserver.py", "--verbose"], env={"LOG_LEVEL": "DEBUG"} ) client = Client(transport) async def listfunc(): # Connect via stdio to a local script async with client: tools = await client.list_tools() print(f"Available tools: {tools}") #stdio async def main(): # Connect via stdio to a local script async with client: result = await client.call_tool("add", {"fp1": 25}) print(f"add函数返回结果: {result.content[0].text}") asyncio.run(main())
2)方式二启动
simpleserver.py程序内改成
if __name__ == "__main__":
mcp.run("sse")
┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ ▄▀▀ ▄▀█ █▀▀ ▀█▀ █▀▄▀█ █▀▀ █▀█ │ │ █▀ █▀█ ▄▄█ █ █ ▀ █ █▄▄ █▀▀ │ │ │ │ FastMCP 2.13.2 │ │ │ │ │ │ 🖥 Server name: myServer │ │ │ │ 📦 Transport: SSE │ │ 🔗 Server URL: http://127.0.0.1:8000/sse │ │ │ │ 📚 Docs: https://gofastmcp.com │ │ 🚀 Hosting: https://fastmcp.cloud │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ [12/03/25 18:49:24] INFO Starting MCP server 'myServer' with server.py:2058 transport 'sse' on http://127.0.0.1:8000/sse INFO: Started server process [41088] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
客户端访问
程序需改为:client = Client("http://localhost:8000/sse")
三、利用mcp中集成的fastmcp,进行下面的测试
1.编写server.py和client.py
client.py import hashlib from loguru import logger from mcp.server.fastmcp import FastMCP mcp = FastMCP("public-demo") @mcp.tool() def generate_md5_hash(input_str: str) -> str: """创建 md5 hash 字符串""" logger.info(f"Generating MD5 hash for: {input_str}") md5_hash = hashlib.md5() md5_hash.update(input_str.encode('utf-8')) return md5_hash.hexdigest() if __name__ == "__main__": mcp.run(transport='stdio') server.py import asyncio import os from langchain_ollama.chat_models import ChatOllama from mcp_use import MCPAgent, MCPClient current_dir = os.path.dirname(os.path.abspath(__file__)) server_path = os.path.join(current_dir, "server2.py") CONFIG = { "mcpServers": { "fii-demo": { "command": "uv", "args": ["run", server_path] } } } #smollm2:135m 这个模型不支持tools #shaw/dmeta-embedding-zh:latest 不支持tools #registry.ollama.ai/library/deepseek-r1:8b does not support tools async def main(): client = MCPClient.from_dict(CONFIG) llm = ChatOllama(model=r"qwen3-vl:2b", base_url="http://localhost:11434") agent = MCPAgent(llm=llm, client=client, max_steps=20) #向agent发送prompt result = await agent.run( "请计算 md5 hash 对于下面的字符串: 'are you ok!' " ) print(r"\nResult:", result) await client.close_all_sessions() if __name__ == "__main__": asyncio.run(main())
2.执行client.py
2025-12-03 22:54:27,557 - mcp_use.telemetry.telemetry - INFO - Anonymized telemetry enabled. Set MCP_USE_ANONYMIZED_TELEMETRY=false to disable. 2025-12-03 22:54:28,032 - mcp_use - INFO - 🚀 Initializing MCP agent and connecting to services... 2025-12-03 22:54:28,033 - mcp_use - INFO - 🔌 Found 0 existing sessions 2025-12-03 22:54:28,033 - mcp_use - INFO - 🔄 No active sessions found, creating new ones... .... 2025-12-03 22:54:29,895 - mcp_use - INFO - 🛠️ Created 1 LangChain tools from client 2025-12-03 22:54:29,895 - mcp_use - INFO - 🧰 Found 1 tools across all connectors 2025-12-03 22:54:29,896 - mcp_use - INFO - 🧠 Agent ready with tools: gen_md5_hash 2025-12-03 22:54:29,903 - mcp_use - INFO - ✨ Agent initialization complete 2025-12-03 22:54:29,903 - mcp_use - INFO - 💬 Received query: '请计算md5 hash对于下面的字符串: 'are you ok!' ' 2025-12-03 22:54:29,903 - mcp_use - INFO - 🏁 Starting agent execution [12/03/25 22:55:06] INFO Processing request of type server.py:674 CallToolRequest 2025-12-03 22:55:06.521 | INFO | __main__:gen_md5_hash:8 - MD5 hash.... : are you ok! 2025-12-03 22:55:06,526 - mcp_use.agents.display - INFO - 🔧 Tool call: gen_md5_hash with input: {'input_str': 'are you ok!'} 2025-12-03 22:55:06,526 - mcp_use.agents.display - INFO - 📄 Tool result: [TextContent(type='text', text='561d369726c9ec8399e0d563638564e5', annotations=None, meta=None)]
下面为依据官网: 加入到了server.py中
# server.py
from fastmcp import FastMCP
from typing import Annotated
from pydantic import Field
from typing_extensions import Literal
from fastmcp.utilities.types import Image, Audio, File
from dataclasses import dataclass
mcp = FastMCP("TestServer")
#创建工具1
@mcp.tool()
def output(name: str) -> str:
print("client调用到output了--------------")
return f"obj1:{name}"
#创建工具2
"""
当此工具注册时,FastMCP 自动:
使用函数名称(add)作为工具名称。
使用函数的文档字符串(将两个整数相加...)作为工具描述。
基于函数的参数和类型注解生成输入架构。
处理参数验证和错误报告。
"""
@mcp.tool
def add(fp1: int, fp2: int) -> int:
"""将两个整数相加。"""
print("client调用到add了--------------")
return fp1 + fp2
#astMCP 支持广泛的类型注解,包括所有 Pydantic 类型:
#工具5
@mcp.tool
def search_products(
query: str, # 必需 - 没有默认值
max_results: int = 10, # 可选 - 因为有默认值
sort_by: str = "relevance", # 可选 - 因为有默认值
category: str | None = None # 可选 - 因为有默认值,可以为 None
) -> list[dict]:
"""搜索产品目录。"""
# 实现...
#在这个例子中,LLM 必须提供 query 参数,而 max_results、sort_by 和 category 如果没有显式提供将使用它们的默认值。
#创建工具3
@mcp.tool(
name="find_products", # LLM 的自定义工具名称
description="搜索产品目录,带有可选的分类过滤。", # 自定义描述
tags={"catalog", "search"}, # 用于组织/过滤的可选标签
meta={"version": "1.2", "author": "product-team"} # 自定义元数据
)
def search_products_implementation(query: str, category: str | None = None) -> list[dict]:
"""内部函数描述(如果上面提供了描述则忽略)。"""
# 实现...
print(f"Searching for '{query}' in category '{category}'")
return [{"id": 2, "name": "Another Product"}]
#工具4
@mcp.tool
def analyze_text(
text: str,
max_tokens: int = 100,
language: str | None = None
) -> dict:
"""分析提供的文本。"""
# 实现...
#对于基本参数描述,您可以使用 Annotated 的便捷简写
#Annotated这个需要typing库支持
@mcp.tool
def process_image(
image_url: Annotated[str, "要处理的图像的 URL"],
resize: Annotated[bool, "是否调整图像大小"] = False,
width: Annotated[int, "目标宽度(像素)"] = 800,
format: Annotated[str, "输出图像格式"] = "jpeg"
) -> dict:
"""使用可选调整大小处理图像。"""
# 实现...
#请将 Pydantic 的 Field 类与 Annotated 一起使用:
#需要pydantic的Field支持
#需要typing_extensions的Literal支持
@mcp.tool
def process_image(
image_url: Annotated[str, Field(description="要处理的图像的 URL")],
resize: Annotated[bool, Field(description="是否调整图像大小")] = False,
width: Annotated[int, Field(description="目标宽度(像素)", ge=1, le=2000)] = 800,
format: Annotated[
Literal["jpeg", "png", "webp"],
Field(description="输出图像格式")
] = "jpeg"
) -> dict:
"""使用可选调整大小处理图像。"""
# 实现...
#您也可以将 Field 用作默认值,但更推荐使用 Annotated 方法:
"""
Field 提供了几个验证和文档功能:
description:参数的人类可读解释(显示给 LLM)
ge/gt/le/lt:大于/小于(或等于)约束
min_length/max_length:字符串或集合长度约束
pattern:字符串验证的正则表达式模式
default:如果省略参数时的默认值
"""
@mcp.tool
def search_database(
query: str = Field(description="搜索查询字符串"),
limit: int = Field(10, description="最大结果数", ge=1, le=100)
) -> list:
"""使用提供的查询搜索数据库。"""
# 实现...
#显示给 LLM 的工具架构中排除某些参数
#(如 state、user_id 或凭据)很有用,这些参数不应该暴露给 LLM 或客户端。只有具有默认值的参数才能被排除;尝试排除必需参数将引发错误。
# user_id 不会出现在工具的参数架构中,但仍可以在运行时由服务器或框架内部设置。
@mcp.tool(
name="get_user_details",
exclude_args=["user_id"]
)
def get_user_details(user_id: str = None) -> str:
# user_id 将由服务器注入,不是由 LLM 提供
...
#from fastmcp.utilities.types import Image, Audio, File
#FastMCP 提供用于返回图像、音频和文件的助手类
#如果您返回 fastmcp.utilities.types.Image 对象,FastMCP 将将其转换为具有正确 MIME 类型和 base64 编码的 MCP ImageContent 块
@mcp.tool
def get_chart() -> Image:
"""生成图表图像。"""
return Image(path="chart.png")
#返回图像和文本
def get_chart_text() -> Image:
"""生成图表图像。"""
return [Image(path="chart1.png"), "text content"]
#类对象结果(自动结构化内容)
@mcp.tool
def get_user_data(user_id: str) -> dict:
"""获取用户数据,无需类型注解。"""
return {"name": "Alice", "age": 30, "active": True}
@mcp.tool
def get_multiple_charts() -> list[Image]:
"""返回多个图表。"""
return [Image(path="chart1.png"), Image(path="chart2.png")]
#返回复杂性类型
#需要 from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
email: str
@mcp.tool
def get_user_profile(user_id: str) -> Person:
"""获取用户档案信息。"""
#返回用户的信息
return Person(name="Alice", age=30, email="alice@example.com")
#您可以通过提供自定义的 output_schema 来覆盖自动生成的架构
@mcp.tool(output_schema={
"type": "object",
"properties": {
"data": {"type": "string"},
"metadata": {"type": "object"}
}
})
def custom_schema_tool() -> dict:
"""具有自定义输出架构的工具。"""
return {"data": "Hello", "metadata": {"version": "1.0"}}
if __name__ == "__main__":
mcp.run()
"""
方式一、
fastmcp run server.py:mcp --transport http --port 8000
[12/03/25 14:21:28] INFO Starting MCP server 'TestServer' with transport 'http' on http://127.0.0.1:8000/mcp server.py:2058
INFO: Started server process [62788]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: 127.0.0.1:41107 - "POST /mcp HTTP/1.1" 200 OK
INFO: 127.0.0.1:41115 - "GET /mcp HTTP/1.1" 200 OK
INFO: 127.0.0.1:41114 - "POST /mcp HTTP/1.1" 202 Accepted
INFO: 127.0.0.1:41117 - "POST /mcp HTTP/1.1" 200 OK
INFO: 127.0.0.1:41119 - "POST /mcp HTTP/1.1" 200 OK
INFO: 127.0.0.1:41123 - "DELETE /mcp HTTP/1.1" 200 OK
启动二、
fastmcp dev server.py (dev模式,端口是6274)
默认是会启动调试器 mcp inspector
http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=76040fffe244e7d70d2e333d79b4fb92dc27a4f9a10660127770002b53220d37#resources
"""
另外:fast-agent-mcp
fast-agent 是一款面向企业级应用的 MCP (Model Context Protocol) 1.22.0 完整实现 的 AI Agent 开发框架。 该框架由 EvalState 开发,旨在解决 AI Agent 开发中的 工具连接、任务编排、人机协作 三大核心痛点,提供从原型到生产的全链路开发支持。
1.安装fast-agent-mcp 包还是比较困难,经过多次反复实验 1).需要openai库支持 2)typer==0.20.支持 3)TensorZero 使你能够根据生产指标和人工反馈优化复杂的 LLM 应用程序。 4)fast-agent-mcp 0.2.49 requires mcp==1.12.1,
#注意fast-aget-mcp不要和fastmcp安装到一个项目里面,否则会依赖mcp包的问题,而fastmcp依赖mcp 1.23.1 Location: C:\Users\king\PycharmProjects\PythonProjectmcp310up\.venv\Lib\site-packages Requires: a2a-sdk, (aiohttp, anthropic, azure-identity, boto3, deprecated, email-validator, fastapi, google-genai, mcp, openai, opentelemetry-distro, opentelemetry-exporter-otlp-proto-http, opentelemetry-instrumentation-anthropic, opentelemetry-instrumentation-google-genai, opentelemetry-instrumentation-mcp, opentelemetry-instrumentation-openai, prompt-toolkit, pydantic, pydantic-settings, pyperclip, pyyaml, rich, tensorzero, typer)
参考:
https://pypi.org/project/mcp/
transport: Transport protocol to use ("stdio", "sse", or "streamable-http")

浙公网安备 33010602011771号