MCP 系列(01):MCP 是什么——为什么 Function Calling 不够

从一个真实问题出发

你给 Claude 接入了 Jira 工具,能查工单、创建 Issue、更新状态。代码跑通了,团队很满意。

三周后,另一个团队的同事想在自己的 Agent 项目里也用 Jira 工具。

你有两个选择:

  1. 把 Jira 工具的代码复制给他
  2. 让他安装一个 MCP Server,5 分钟接入完成

MCP 的设计目标就是让第二个选项成为默认。


Function Calling 的能力边界

Function Calling(也叫 Tool Use)让 LLM 能够调用外部工具。原理很直接:

python 复制代码
# 定义工具 schema
tools = [{
    "name": "search_jira",
    "description": "搜索 Jira 工单",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string", "description": "搜索关键词"},
            "project": {"type": "string", "description": "项目 Key,可选"}
        },
        "required": ["query"]
    }
}]

# 调用 LLM,附带工具定义
response = client.messages.create(
    model="claude-sonnet-4-6",
    tools=tools,
    messages=[{"role": "user", "content": "查一下最近的 P0 Bug"}]
)

# 如果 LLM 决定调用工具
if response.stop_reason == "tool_use":
    tool_call = response.content[0]
    result = search_jira(tool_call.input["query"])
    # 把结果返回给 LLM 继续推理

这套机制功能完整,已经在生产环境广泛使用。

但它有一个结构性问题:工具定义和工具实现都在调用侧。

每个用 Jira 工具的项目都需要:

  • 维护一份 search_jira 的 schema 定义
  • 维护一份 search_jira 的执行逻辑
  • 处理认证、错误重试、结果格式化

三个团队,三份重复的代码。Jira API 更新了,三个地方都要改。


MCP 的核心设计

MCP(Model Context Protocol)把工具定义从调用侧移到了独立的 Server 进程。

vbscript 复制代码
Function Calling 模型:
┌─────────────────────────────────────┐
│  你的 Agent 代码                     │
│  ├── LLM 调用逻辑                    │
│  ├── 工具 schema 定义               │  ← 每个 Agent 自己维护
│  └── 工具执行逻辑(Jira/GitHub/...) │  ← 每个 Agent 自己维护
└─────────────────────────────────────┘

MCP 模型:
┌──────────────────┐     ┌─────────────────────┐
│  Host(Agent/IDE)│────▶│  MCP Server          │
│  只写调用逻辑     │     │  ├── 工具 schema 定义 │  ← 统一维护
└──────────────────┘     │  └── 工具执行逻辑     │  ← 统一维护
                         └─────────────────────┘

MCP Server 是一个独立运行的进程,通过标准协议(JSON-RPC 2.0)与 Host 通信。任何支持 MCP 的 Host------Claude Desktop、Claude Code、自研 Agent------都可以直接连接这个 Server,无需重新实现工具逻辑。


同一个场景,两种实现

以"搜索 Jira 工单"为例,对比两种方案的完整代码结构。

方案 A:Function Calling

python 复制代码
# agent.py ------ 工具定义和执行逻辑都在这里

import anthropic
from jira import JIRA

jira_client = JIRA(
    server="https://your-company.atlassian.net",
    basic_auth=("user@example.com", os.environ["JIRA_API_TOKEN"])
)

# 工具定义(schema)
JIRA_TOOLS = [{
    "name": "search_jira",
    "description": "搜索 Jira 工单,返回标题、状态、优先级",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string"},
            "max_results": {"type": "integer", "default": 10}
        },
        "required": ["query"]
    }
}]

# 工具执行逻辑
def execute_tool(name: str, args: dict) -> str:
    if name == "search_jira":
        issues = jira_client.search_issues(
            f'text ~ "{args["query"]}"',
            maxResults=args.get("max_results", 10)
        )
        return "\n".join([
            f"[{i.key}] {i.fields.summary} ({i.fields.status.name})"
            for i in issues
        ])
    raise ValueError(f"Unknown tool: {name}")

# Agent 主循环
def run_agent(user_input: str):
    client = anthropic.Anthropic()
    messages = [{"role": "user", "content": user_input}]
    
    while True:
        response = client.messages.create(
            model="claude-sonnet-4-6",
            tools=JIRA_TOOLS,
            messages=messages
        )
        if response.stop_reason != "tool_use":
            return response.content[0].text
        
        tool_call = next(b for b in response.content if b.type == "tool_use")
        result = execute_tool(tool_call.name, tool_call.input)
        
        messages.extend([
            {"role": "assistant", "content": response.content},
            {"role": "user", "content": [{"type": "tool_result",
                                           "tool_use_id": tool_call.id,
                                           "content": result}]}
        ])

方案 B:MCP Server

python 复制代码
# jira_mcp_server.py ------ 独立进程,任意 Host 可用

import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
from jira import JIRA

jira_client = JIRA(
    server="https://your-company.atlassian.net",
    basic_auth=("user@example.com", os.environ["JIRA_API_TOKEN"])
)

server = Server("jira-tools")

@server.list_tools()
async def list_tools() -> list[Tool]:
    return [Tool(
        name="search_jira",
        description="搜索 Jira 工单,返回标题、状态、优先级",
        inputSchema={
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "max_results": {"type": "integer", "default": 10}
            },
            "required": ["query"]
        }
    )]

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "search_jira":
        issues = jira_client.search_issues(
            f'text ~ "{arguments["query"]}"',
            maxResults=arguments.get("max_results", 10)
        )
        result = "\n".join([
            f"[{i.key}] {i.fields.summary} ({i.fields.status.name})"
            for i in issues
        ])
        return [TextContent(type="text", text=result)]

if __name__ == "__main__":
    asyncio.run(stdio_server(server))
python 复制代码
# agent.py ------ 只剩 Agent 逻辑,工具部分消失了

# MCP 的工具发现和调用由框架处理
# 不需要在这里定义任何 Jira 相关的代码

第二个团队接入:

bash 复制代码
# 安装
pip install jira-mcp-server

# Claude Code 配置(~/.claude/settings.json 或项目 .claude/settings.json)
# 添加 MCP server 配置后,工具自动可用,无需改一行 Agent 代码

核心差异对比

vbscript 复制代码
维度              Function Calling          MCP
──────────────────────────────────────────────────────────────────
工具定义位置       调用侧代码中               独立 Server 进程
复用方式           复制代码                   安装 / 连接 Server
跨项目复用         手动同步,容易漂移          Server 版本统一
跨模型使用         每个模型分别配置            一份 Server,任意 Host
状态管理           无(每次调用无状态)         Server 可维护连接状态
部署成本           无额外进程                 需要管理 Server 进程
适合规模           1-2 个项目使用同一工具      多个项目/团队共享工具

MCP 是 Function Calling 的上层标准化层,两者协同工作。MCP Server 内部的工具调用,仍然通过 Host 与 LLM 之间的 Function Calling 机制完成。


什么时候用 MCP,什么时候 Function Calling 就够

直接用 Function Calling:

  • 工具只有一个项目使用,没有复用需求
  • 工具逻辑简单,10-20 行代码搞定
  • 快速原型,不需要额外维护一个进程
  • 工具与 Agent 逻辑高度耦合,分离意义不大

上 MCP Server:

  • 工具需要跨多个 Agent 项目复用(Jira / GitHub / 数据库)
  • 工具有状态(连接池、缓存、Session)
  • 需要让 Claude Desktop / Claude Code 这类 Host 直接使用
  • 企业内部统一工具标准,多团队共享

判断规则:

vbscript 复制代码
这个工具会被超过 2 个项目使用吗?
  → 是 → 考虑 MCP Server
  → 否 → Function Calling 足够

工具需要维护连接状态吗(数据库连接、认证 Session)?
  → 是 → MCP Server(Server 进程管理状态)
  → 否 → 继续下一个问题

非工程师也需要能配置和使用这个工具吗?
  → 是 → MCP Server(参考 Claude Desktop 生态)
  → 否 → Function Calling 就够

总结

MCP 解决的不是"能不能调工具"的问题------Function Calling 已经解决了这个。MCP 解决的是"工具如何标准化复用"的工程问题:把工具从每个 Agent 项目里分离出来,放进独立的 Server,让任何支持 MCP 协议的 Host 都能直接使用。

代价是引入了进程管理的复杂度。收益是工具只维护一份,所有依赖方自动获得更新。

下一篇进入 MCP 协议本身:Host / Client / Server 三层模型,JSON-RPC 消息格式,以及用 MCP Inspector 调试一次真实的通信过程。


参考资料


欢迎访问 PrimeSkills ------ 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。

更多实用知识和有趣产品,欢迎访问我的个人主页

相关推荐
冬奇Lab1 小时前
每日一个开源项目(第154篇):Warp - 从‘好看的终端‘到 Agentic 开发环境
人工智能·rust·llm
ZZZMMM.zip1 小时前
基于鸿蒙HarmonyOS NEXT开发AI股票分析应用:智能投资新体验与鸿蒙Flutter框架跨端实践
人工智能·flutter·华为·harmonyos·鸿蒙
龙腾亚太2 小时前
当大语言模型遇上USV集群:大模型驱动的自适应路径规划方法
人工智能·语言模型·自然语言处理
米小虾2 小时前
从RAG到GraphRAG:2026年知识增强生成技术的进化之路与实战指南
人工智能·agent
珠海西格电力2 小时前
云边端协同架构:零碳园区管理系统的技术底座
大数据·运维·人工智能·算法·架构·能源
字节跳动视频云技术团队2 小时前
Agent 进化论:从对话到协作
人工智能·agent·音视频开发
犀利豆2 小时前
AI in Harness(三)
人工智能·后端
Esaka_Forever2 小时前
MCP Prompt(模型上下文协议 提示模板)完整详解
mcp