给大忙人看的MCP Tutorial

MCP是个啥?

{width=”5.75in” height=”3.2291666666666665in”}

MCP 全称 Model Context Protocol,于 2024 年 11 月由Anthropic推出顾名思义,它是一组协议,更具体来讲是一组用于规定标准化LLM如何与外部信息源连接的规则。可以将 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 为设备连接各种外设和配件提供了标准化方式一样,MCP 为 AI 模型连接不同的数据源和工具提供了标准化方式。

MCP架构

- WIP

MCP VS. Function Calling(可选)

什么是Function Calling ?

在不启用联网搜索的情况下,我们询问豆包:

{width=”5.75in” height=”1.6875in”}

可以看到LLM的自身数据截止到2024年10月, 如果这个问题需要人类帮助LLM来解决,那么一个有效的操作步骤可以是:

+————————————————————————————————————————————————–+
| 选择工具 |
| |
| 由于问题是关于实时新闻的,打开浏览器工具; |
| |
| 提取参数 |
| |
| 由于提问的问题是”niko在哪个战队?”,用浏览器输入框查询”niko在哪个战队?”; |
| |
| 运行工具 |
| |
| 浏览器返回了许多网页,如”NiKo转会Falcons:争议与期望并存“等; |
| |
| 将工具的输出提供给大模型 |
| |
| 将网页内容输入到提示词对通义千问 API 进行提问:”这是查询到的信息:NiKo转会Falcons:争议与期望并存......。请总结并回答:2025年Niko在哪个战队?” |
+————————————————————————————————————————————————–+

由于向大模型提供了足够的参考信息,可以得到类似的回复:


JSON
call_result
2025 年,NiKo 在 Falcons(猎鹰)战队。

2024 年 8 月,有外媒爆料称 G2 战队的 NiKo 将加盟 Falcons 战队。2024 年 10 月,有消息称 NiKo 加盟后的首秀帮助 Falcons 团队斩获 PGL 布加勒斯特站冠军。2025 年 1 月 3 日,沙特电竞俱乐部 Falcons 正式官宣 NiKo 加盟其 CS2 分部


LLM此时可以回答关于实时新闻的问题了,然而这个过程需要人的参与(选择工具、提取参数、运行工具),无法实现自动化运行的效果。

Function Calling(工具调用)可以自动化完成以上流程,在接收到提问后可以自动选择工具、提取参数、运行工具并总结工具的输出

这二者有何不同?

虽然Function Calling让 LLM的能力变强了很多,但它也有不少问题需要解决:

+——————————————————————————————————————————————————————————————————————————————————————————————————————————————-+
| function只能提前设定,不够灵活 |
| |
| 现在的 LLM只能用我们提前教给它的功能。比如你提前教它查天气、算数学题,它就只会这两样。如果突然问它 "怎么坐大巴去二仙桥然后在二仙桥跳广场舞最后在二仙桥安放C4炸弹",因为没提前教过相关功能,它要么答不上来,要么只能随便说点通用的话。遇到需要分步骤解决的复杂问题或者需要创意的任务,固定的功能列表会限制它的发挥,很难灵活应对新需求。 |
| |
| function太多时,LLM会 "记混" |
| |
| 当提供的功能超过几十个或更多,模型就会出现两个问题:一是prompt太长,超过它能 "记住" 的内容长度;二是容易把功能搞混,比如把 "查天气" 的功能当成 "订外卖" 来用。这时候就需要给功能分类,否则LLM最终会乱套。 |
| |
| 容易 "犯低级错误" |
| |
| > LLM在使用function时可能犯两种错: |
| |
| 用错function:比如用户想算乘法,它却调用了查天气的功能。(当然并不会这么傻,比喻,现实场景往往会更复杂) |
| |
| 填错参数:比如让它用计算器算 "3 乘 5",它可能写成 "3 加 5",或者把格式搞错(比如本该写 JSON 格式却写错符号)。这些错误需要我们来介入,并做额外的检查和修正。 |
| |
| 多步骤任务又慢又”expensive” |
| |
| 每次call function都要走流程:AI 先输出指令→程序执行function→把结果返回给 AI,一来一回需要时间。如果一个任务需要走10 步function callin,就需要来回沟通 10 次,不仅速度会变慢,每次沟通都要消耗tokens,成本也会变高。 |
| |
| 不会自己发现new function |
| |
| LLM只能用当前对话中提前告诉它的function,就像一个只会按菜单点菜的服务员,菜单上没有的菜就不会做。如果外部有新function上线,必须手动更新它的 "function list",它自己不会主动发现和学习新能力,灵活性很差。 |
+——————————————————————————————————————————————————————————————————————————————————————————————————————————————-+

总体来说,function calling让 AI 更能干,但也像个 "死板的工具人":只能用固定工具、功能多了会乱、容易出错、做复杂事又慢又贵,还不会自己学新东西

那MCP可以做到上述几点吗?我们可以把 MCP 想象成一个”智能升级”的AI助手,它针对function calling的痛点,做了实实在在的改进:

+————————————————————————————————————————————————————————————————+
| MCP允许AI在运行时通过tools/list接口查询服务器支持的工具列表,无需提前硬编码所有函数。例如,新增”文件删除”功能时,AI可自动发现并使用,无需人工更新提示。 |
| |
| 工具只需实现一次MCP server,即可被所有兼容AI client调用(如Claude、IDE插件),避免为不同LLM重复开发函数,解决”M×N集成问题”。 |
| |
| MCP server返回的工具列表包含输入模式(如参数类型、格式),AI可根据结构化信息生成更准确的调用指令,减少参数错误,并且client可在执行前验证工具调用格式(如JSON正确性),部分错误可在协议层拦截。 |
| |
| MCP支持原生动态发现,这也是本质上的区别。AI可通过protocol主动查询server,实时感知工具增减或升级。例如,server新增”图像生成”功能后,AI无需人工干预即可使用。 |
+————————————————————————————————————————————————————————————————+


维度 Function Calling局限 MCP的改进点

灵活性 固定函数限制,难处理复杂流程 动态工具发现+自主多步规划

可扩展性 函数数量多导致上下文爆炸 标准化协议+按需数据加载

动态适应性 无法感知新工具,需人工更新 runtime工具查询+自动协议适配

错误处理 依赖人工校验参数和格式 结构化工具描述+协议层校验


Make your hands dirty

构建你的第一个空间透镜

让我们在实践中快速上手MCP


如果你想要完整的了解MCP,请移步:https://modelcontextprotocol.io/introduction,这里只做快速上手指南,旨在让用户快速搭建一个自己的MCP service


首先介绍一下MCP server端为我们提供的3种原语:


原语 控制方 描述

Prompts 用户控制 由用户选择调用的交互式模板

Resources 应用控制 由客户端附加并管理的上下文数据

Tools 模型控制 暴露给 LLM 以便其执行操作的功能接口


这里我们用到了Tools


code skeleton:



python
code skeleton
import os
from MCP.server.fastMCP import FastMCP

# 创建一个MCP服务器实例
MCP = FastMCP("StorageAnalyzer")

# @MCP.tool() decorator将这个函数注册为MCP工具
@MCP.tool()
def get_storage_usage(path: str = '.') -> dict:
return analyze_storage(path)

# 启动MCP服务器
if __name__ == "__main__":
MCP.run()


完整代码如下:


python
server.py
import os
from mcp.server.fastmcp import FastMCP

def analyze_storage(path: str) -> dict:
if not os.path.exists(path):
raise ValueError(f"Path not found: {path}")
if not os.path.isdir(path):
raise NotADirectoryError(f"Not a dir: {path}")
storage_data = {}
file_type_stats = {}
for root, dirs, files in os.walk(path):
dir_size = 0
for file in files:
file_path = os.path.join(root, file)
try:
file_size = os.path.getsize(file_path)
dir_size += file_size
storage_data[file_path] = file_size
EXT_MAPPING = {
'.jpeg': '.jpg',
'.htm': '.html',
'.tiff': '.tif'
}
ext = os.path.splitext(file)[1].lower() or 'no_ext'
ext = EXT_MAPPING.get(ext, ext)
if ext not in file_type_stats:
file_type_stats[ext] = 0
file_type_stats[ext] += file_size
except PermissionError:
storage_data[file_path] = 'Permission denied'
storage_data[root] = dir_size
file_type_human = {}
for ext, size in file_type_stats.items():
if size >= 1e9:
file_type_human[ext] = f'{size/1e9:.2f} GB'
elif size >= 1e6:
file_type_human[ext] = f'{size/1e6:.2f} MB'
elif size >= 1e3:
file_type_human[ext] = f'{size/1e3:.2f} KB'
else:
file_type_human[ext] = f'{size} B'
storage_data['file_type_stats'] = {
'raw_bytes': file_type_stats,
'human_readable': file_type_human
}

storage_data['large_files'] = []
LARGE_FILE_THRESHOLD = 100 * 1024 * 1024
for file_path, size in storage_data.items():
if isinstance(size, int) and size > LARGE_FILE_THRESHOLD and not os.path.isdir(file_path):
storage_data['large_files'].append({
'path': file_path,
'size_bytes': size,
'size_human': f'{size/1e9:.2f} GB' if size >= 1e9 else f'{size/1e6:.2f} MB'
})
return storage_data

mcp = FastMCP("StorageAnalyzer")

@mcp.tool()
def get_storage_usage(path: str = '.') -> dict:
return analyze_storage(path)

if __name__ == '__main__':
mcp.run()


你可以在当前工作区新建一个server.py,将上面的代码copy进去。

代码编写好后,在 Trae 里选择"设置",再选择 MCP,点击添加

{width=”5.75in” height=”1.4166666666666667in”}

点击手动配置

{width=”5.75in” height=”1.2708333333333333in”}

在这里配置我们的空间透镜

{width=”5.75in” height=”2.53125in”}

参考如下:


json
{
"mcpServers": {
"spacelens": {
"command": "python",
"args": [
"$YOUR_SERVER_PATH" // 你的server文件位置
]
}
}
}


配置好后,返回MCP页面,点击这里就可以添加我们的agent with space_lens:

{width=”5.75in” height=”1.7916666666666667in”}

效果如图:

{width=”5.75in” height=”2.8020833333333335in”}

Have fun!