ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MCP 是什么?与 API、插件、Agent 有什么区别:从一个缺陷查询案例讲起

MCP 是什么?与 API、插件、Agent 有什么区别:从一个缺陷查询案例讲起

MCP 实战第 1 篇。先厘清概念、角色与业务边界,再运行一个不需要模型密钥的本地练习。本文不安装 MCP SDK,不启动 MCP 服务,也不声称已经接入任何 AI 客户端。

在前面的 Dify 系列中,我们已经搭建应用、调用工作流,并给业务后端补上了任务管理。接下来一个很自然的需求是:让 AI 查询项目缺陷、读取团队文档,再把相关信息整理成一份测试建议。

这时常常会听到四种说法:“调一下 API”“装个插件”“接一个 MCP”“做成 Agent”。它们可能出现在同一套系统中,却各自解决不同问题。如果一开始就混在一起,后面很容易把配置成功当成业务可用,把模型说“查到了”当成实际访问过系统。

本文用一个贯穿案例展开:团队研发助手查询某个项目尚未关闭的缺陷,并按原始记录生成摘要。第一步只允许查询,创建和修改缺陷放到后续有权限控制的章节再讨论。

一、从业务需求开始理解 MCP

假设测试人员问:“帮我看看演示项目有哪些未关闭的缺陷。”要回答这句话,系统至少得完成下面几件事:识别项目,找到能够查询缺陷的能力,把参数交给实际程序执行,取得记录,再整理答案。

语言模型本身不会因为听到项目名称,就自然拥有你公司缺陷平台的登录权限。即使模型能根据上下文组织出一段像样的报告,也不代表这些缺陷真的存在。我们需要一条能检查执行结果的信息通道。

MCP 的全称是 Model Context Protocol。它约定 AI 应用与提供工具、资料等能力的程序怎样交换信息。官方将其定位为上下文交换协议,模型推理与上下文的具体管理仍属于 AI 应用的职责。MCP 官方架构概览

放到这个案例里,我们可以编写一个缺陷查询服务,让它暴露list_demo_issues这样的工具。支持相关能力的 AI 客户端接入后,可以发现工具的名称和参数结构,并通过协议请求执行。

真正查数据的仍然是服务端业务代码。它可以访问本地样例,也可以在正确授权后调用缺陷平台 API。MCP 不会替你生成真实业务权限,也不会让一个查询函数自动变成完整的测试平台。

二、API、插件、MCP、Agent 分别处在哪一层

为了方便讨论,本文采用下面这组工程口径。尤其“插件”和“Agent”在不同产品中有不同含义,不能把这张表理解为所有厂商共同遵守的定义。

名称主要关注的问题放到缺陷查询案例里
API程序通过什么接口请求另一项能力缺陷平台提供查询接口
插件某个宿主产品怎样扩展功能客户端安装一个缺陷平台扩展
MCPAI 应用怎样按统一协议发现、访问相关能力服务暴露可查询的工具及其参数
Agent系统怎样围绕目标选择动作、观察结果并继续处理判断要先查缺陷,再读取需求,最后整理建议

这里的 API 不特指 HTTP。我们后续接第三方平台时,会经常用到 HTTP API,因此也会遇到地址、认证头、状态码等内容。

插件则取决于宿主产品提供的扩展机制。一个插件可以直接调用 HTTP API,也可能包含 MCP 接入逻辑。只看到“插件安装成功”,还需要继续检查它提供了哪些能力、实际使用哪一套凭据。

MCP 服务也可能只是已有 API 外面的一层适配:将平台接口整理成适合 AI 使用的工具描述、输入约束与结果。平台 API 仍然存在,业务错误和权限校验也仍然需要处理。

至于 Agent,本文关注的是执行过程:模型或程序根据目标选择下一步,宿主检查请求,工具完成操作,然后系统根据观察结果决定继续或结束。它可以使用 MCP 工具,也可以只使用普通函数。接入 MCP 后是否需要自主规划,要由具体任务决定。

例如“读取一个已知 ID 的缺陷并展示字段”,普通脚本可能已经足够。若需求是“先理解问题、检索相关缺陷,再决定还缺哪些信息”,才有必要讨论更复杂的任务组织方式。

三、谁在连接谁:Host、Client、Server

MCP 架构中的 Host、Client、Server 分别承担应用管理、协议连接与能力提供的职责。它们是逻辑角色,不需要机械地理解成三台机器。官方架构说明

角色本文案例中的职责
Host承载交互,组织模型、连接配置与工具执行策略
Client在 Host 一侧与某个 MCP Server 进行协议通信
Server暴露缺陷查询能力,执行业务校验并返回结果

可以把一次查询画成下面这条路径。箭头只表示业务交互关系,省略了版本协商、错误处理和具体传输细节:

测试人员提出问题 ↓ AI 应用 Host:识别意图,组织工具请求与权限检查 ↓ Host 内的 MCP Client ↓ MCP 协议通信 缺陷查询 MCP Server ↓ 本地演示数据 / 经过授权的缺陷平台 API ↓ 结构化结果 → Host → 整理给用户的回答

不要把模型生成的工具调用意图等同于实际执行。宿主可能拒绝该调用,服务器也可能因参数错误或权限不足返回失败。只有工具实际返回可核验的结果,系统才有相应的数据依据。

同样,Server 可以是本机进程,也可以运行在远程环境。官方列出 stdio 与 Streamable HTTP 等传输方式;具体接入时需要匹配客户端、服务实现和协议版本。官方传输说明

本系列不会在这一篇手写握手消息,避免把某个版本的底层示例当成所有客户端都能照抄的配置。后续进入安装篇时,再明确采用的 SDK、运行环境和验证方式。

四、Tools、Resources、Prompts 怎样选

一个服务不必同时实现所有能力。先确定业务需要,再选择合适的表达方式。

Tools 用于明确的操作。查询缺陷、计算统计或创建记录,都可以设计成工具。名称、描述和参数结构应让调用方知道“做什么、需要什么、会得到什么”。官方工具规范定义了tools/list和tools/call等操作。Tools 规范

在本文中,list_demo_issues只查询固定虚构项目。这里特意保留了 demo 字样,让操作者不容易把样例误当成真实生产数据。

Resources 用于提供上下文资料。比如项目说明、字段字典和接口文档,可以作为资源供应用读取。读取哪些内容、如何放进模型上下文,由宿主应用组织,不能假定服务器提供的全部资料都会自动进入每轮对话。Resources 规范

Prompts 用于提供可复用的提示模板。例如“根据给定缺陷记录生成测试风险摘要”,可以预先约定输入和输出要求。模板可以帮助表达任务,但不能代替查询程序,也不能凭空补齐缺失的数据。Prompts 规范

团队需求本文建议的起点
按项目和状态查询缺陷一个查询 Tool
提供团队字段说明一份 Resource
统一风险摘要的组织方式一份 Prompt
判断是否还要查询其他资料Host 或 Agent 的任务组织逻辑

具体客户端可能只实现其中一部分,界面呈现也可能不同。不能只凭“支持 MCP”就认定它完整支持某个服务器的全部能力。

五、先把一个工具的职责写清楚

现在回到团队研发助手。第一版只解决一件事:读取固定演示项目中的 open 或 closed 缺陷,限制返回数量,并明确标识结果来自本地样例。

我们先给业务函数约定以下输入和输出:

项目约定
项目固定为 demo-project
状态open 或 closed,默认 open
数量整数 1~20,默认 10
返回数据来源、查询条件、返回条数、缺陷列表
副作用不写文件,不调用外部平台,不修改缺陷

不要一开始就提供“执行任意 SQL”“请求任意网址”这样的入口。对于当前案例,它们增加了实现范围,却没有帮助我们更清楚地表达缺陷查询需求。

下面是单个工具定义的示意,保存为mcp-01-tool-definition-demo.json。它展示名称、描述和输入 schema,不是完整协议响应,也不能单独作为客户端配置文件使用。此文件没有被下文函数自动加载,默认值和校验分别由 Python 实现;真正注册时再检查两者是否一致。

{"name":"list_demo_issues","description":"查询固定虚构项目中的演示缺陷。只返回本地样例,不访问真实缺陷平台。","inputSchema":{"type":"object","properties":{"project":{"type":"string","enum":["demo-project"]},"status":{"type":"string","enum":["open","closed"],"default":"open"},"limit":{"type":"integer","minimum":1,"maximum":20,"default":10}},"required":["project"],"additionalProperties":false}}

参数里的 project 只是调用者提供的查询条件。即使 schema 将它限制为固定值,也不能把这个设计推广成真实鉴权机制。接入真实平台时,要根据可信登录身份在服务端决定允许访问哪些项目。

六、完整本地练习:先验证业务函数

保存为mcp-01-business-demo.py。练习只使用 Python 标准库,建议使用 Python 3.10 或更新版本;本次实际运行环境为 macOS、Python 3.13.3。

"""Local business-function exercise only. Not an MCP server or protocol client."""importargparseimportjson PROJECT="demo-project"ISSUES=({"id":"DEMO-001","title":"演示:提交按钮重复响应","status":"open"},{"id":"DEMO-002","title":"演示:报告标题显示异常","status":"open"},{"id":"DEMO-003","title":"演示:空列表提示缺失","status":"closed"},)deflist_demo_issues(project,status="open",limit=10):# A fixed demo project is a fixture boundary, not a real authorization system.ifproject!=PROJECT:raisePermissionError("demo_project_only")ifnotisinstance(status,str)orstatusnotin{"open","closed"}:raiseValueError("invalid_status")iftype(limit)isnotintornot1<=limit<=20:raiseValueError("invalid_limit")rows=[dict(row)forrowinISSUESifrow["status"]==status][:limit]return{"source":"local-fixture","project":PROJECT,"status":status,"count":len(rows),"issues":rows}defmain():parser=argparse.ArgumentParser(description=__doc__)parser.add_argument("--project",required=True)parser.add_argument("--status",default="open",choices=["open","closed"])parser.add_argument("--limit",default=10,type=int)args=parser.parse_args()try:result=list_demo_issues(args.project,args.status,args.limit)except(ValueError,PermissionError)aserror:print(json.dumps({"error":str(error)}))return2print(json.dumps(result,ensure_ascii=False,indent=2))return0if__name__=="__main__":raiseSystemExit(main())

这个文件只是后续准备封装的业务能力,目前没有 MCP 通信层。它不使用模型,不会出现在任何客户端的工具列表里,也没有启动监听服务。

Mac 终端中,在保存文件的目录执行:

python3 mcp-01-business-demo.py--projectdemo-project--statusopen--limit10

Windows PowerShell 中:

py-3 mcp-01-business-demo.py--project demo-project--status open--limit 10

如果 Windows 中的 Python 安装没有提供 py 启动器,使用自己的 Python 解释器执行同一文件。这里不要求安装 Docker、Node.js 或客户端应用。

输出应包含source=local-fixture、count=2,以及 DEMO-001、DEMO-002 两条 open 记录。将--status改为 closed,会得到 DEMO-003;将--limit改为 1,只返回一条。

再执行一次错误项目查询:

python3 mcp-01-business-demo.py--projectother-project

预期输出为下面的业务拒绝,进程退出码为 2;Windows 只需替换解释器命令。

{"error":"demo_project_only"}

这组限制让练习具有确定的边界,但还没有真实用户、项目权限或远程认证。不要把它包装成“已经完成安全的企业查询服务”。

七、给业务能力保留一份可重复检查

保存为mcp-01-test-business-demo.py,与业务脚本放在同一个目录。测试检查查询、过滤、数量限制、非法参数和结果副本,不会连接任何真实设备或服务。

"""Run beside mcp-01-business-demo.py. All records are local fixtures."""importimportlib.utilfrompathlibimportPathimportunittest spec=importlib.util.spec_from_file_location("business",Path(__file__).with_name("mcp-01-business-demo.py"))business=importlib.util.module_from_spec(spec)spec.loader.exec_module(business)classBusinessTests(unittest.TestCase):deftest_open_issues(self):result=business.list_demo_issues("demo-project")self.assertEqual(result["count"],2)self.assertEqual(result["source"],"local-fixture")self.assertTrue(all(row["status"]=="open"forrowinresult["issues"]))deftest_closed_issues(self):self.assertEqual(business.list_demo_issues("demo-project","closed")["issues"][0]["id"],"DEMO-003")deftest_limit(self):self.assertEqual(business.list_demo_issues("demo-project",limit=1)["count"],1)deftest_other_project_rejected(self):withself.assertRaises(PermissionError):business.list_demo_issues("other-project")deftest_invalid_status(self):forstatusin("all",[],None):withself.subTest(status=status),self.assertRaises(ValueError):business.list_demo_issues("demo-project",status)deftest_invalid_limit(self):forlimitin(0,21,True,1.0,"2"):withself.subTest(limit=limit),self.assertRaises(ValueError):business.list_demo_issues("demo-project",limit=limit)deftest_result_does_not_mutate_fixture(self):result=business.list_demo_issues("demo-project")result["issues"][0]["title"]="changed"self.assertNotEqual(business.list_demo_issues("demo-project")["issues"][0]["title"],"changed")if__name__=="__main__":unittest.main(verbosity=2)

运行方式:

python3 mcp-01-test-business-demo.py

Windows 使用py -3 mcp-01-test-business-demo.py。本次实际结果为 7 个测试通过。这能证明本地函数满足当前样例约定,不能证明 MCP 客户端发现、协议协商和真实模型工具选择已经通过。

先把业务函数和协议适配分开,有助于以后定位问题:函数直接执行都失败,先修业务实现;函数正确而客户端看不到工具,再检查服务注册和连接配置;客户端看到工具却没有调用,还需要查看描述、上下文、权限与模型行为。

八、什么时候值得引入 MCP

如果你只有一个固定脚本,每天由一个定时任务执行,普通函数或 API 调用可能更容易维护。选择 MCP 的理由,应来自实际接入需求,例如希望让不同的兼容 AI 客户端发现同一组能力,或者将业务工具独立维护。

对本文团队助手而言,先完成一个只读工具,比一次接通文档、数据库、缺陷创建、通知发送更便于验收。前者能逐项检查输入条件、实际执行和返回记录;后者遇到问题时很难判断卡在哪一层。

选择接入方式前,可以回答四个问题:谁会使用这个工具,数据归谁管理,允许读写哪些内容,以及发生错误时怎样拿到执行证据。答案不明确时,增加协议层通常不会自动解决这些问题。

九、不要把“接通了”当作“能放心使用”

一个真实的查询链路至少要分别验收:服务是否能启动、客户端能否发现工具、传入参数是否经过校验、是否按用户身份限制项目、返回内容是否来自真实请求,以及最后答案是否忠实于记录。

后续还需要区分三种情况:查询成功但没有记录,查询失败,以及返回结果被限制了数量。它们应有不同证据,不能都用一句“没有发现问题”带过。本篇的 count 仅表示实际返回条数,不表示整个项目缺陷总量。

写操作则需要独立设计。比如创建缺陷时,网络超时后是否已经创建成功、重复请求是否会多建一条、操作者能否修改这个项目,都属于具体业务合同。MCP 不会自动提供这些业务保证。

同样,工具返回的文档或缺陷描述是业务数据,里面即使出现“忽略规则”等文字,也不能自动变成新的操作授权。这个原则要体现在宿主的任务策略和工具的权限检查中,不能只放在提示词结尾作为一句口号。

十、这一篇完成了什么,下一篇写什么

这一篇已经把一个抽象需求拆成可讨论的连接关系,并准备了一份可运行、可检查的业务函数。没有发起真实 MCP 调用,没有使用大模型密钥,没有接入缺陷平台,也没有在 Windows 实机执行。

下一篇进入《MCP 开发环境搭建:Mac 与 Windows》,核对选用的 Python SDK 和客户端支持,再准备真正的服务与连接验证。官方 Python SDK 的实现与说明会持续变化,安装时应锁定明确版本,不要把本篇的本地函数直接当成 SDK 示例。官方 Python SDK

后续仍以团队研发助手为主线,依次加入工具注册、文件资料、HTTP 查询、数据库只读访问、权限、日志和部署。MySQL MCP、桌面客户端与浏览器工具会在相应章节结合具体场景展开;本系列先把基础连接与验证方法补齐。

核对日期:2026-10-29。角色、传输和服务器能力的具体引用见各节;固定版本链接用于说明对应版本的概念,不代表本篇已经安装了该协议版本的 SDK。客户端配置和协议消息细节会在后续实操篇单独固定与验证。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进