先说结论:
MCP 是 Anthropic 把 function call 平台化的一步棋,本质是工具接口的统一协议,目标是让 Claude 自动调全世界的工具,谁先做生态,谁就是 AI 里的 App Store。
仓库地址:https://github.com/muggle-stack/MCP.git
背景
MCP是什么
MCP,全称 Model Context Protocol,是 Anthropic 推出的一个“工具调用标准协议”。你可以把它理解成是 function call 的延伸——它还是 function call,只不过是 官方给了一套“统一调用格式 + 运行框架 + 工具注册机制”,把原本碎片化的工具调用给规范起来了。
它解决了什么问题?
目前每家大模型厂商(OpenAI、Anthropic、Google、阿里等)都有自己的 function call 实现,但:
- 格式不一样;
- 参数结构不统一;
- 工具要“为某个模型单独写一次”。
结果就是:工具没法复用,生态根本串不起来。
MCP 就是来统一这套工具调用接口的,让工具开发者可以只写一份 MCP Server,任何支持 MCP 协议的模型(目前是 Claude)都能用上这个工具。
Anthropic 在下什么棋?
其实本质上,MCP 就是 Anthropic 在用 function call 构建一个“插件生态”,而 MCP Server 就是你开发的“插件”。他做这事是为了:
- 给 Claude Desktop 引流;
- 建立 Claude 专属工具平台;
- 把 AI Agent 的“动手能力”交给 MCP 来打通。
可以预见,只要 MCP 工具数量越多,Claude 越能处理复杂任务,最终实现真正的 AI Agent。而工具生态一旦成型,其他厂商就很难插进来了——因为大家都把工具写在 MCP 上了,这是平台级的护城河。
这事靠不靠谱?
目前 Claude 的 function call 能力本身就强,加上这个 MCP 架子一搭,开发者只需要实现一套工具的服务端(就是 MCP Server),就能被 Claude 自动识别调用。
这不是 demo 概念,而是已经能跑、官方支持、有实战意义的东西。MCP 如果发展起来,它很可能会成为 Claude 的“Agent 能力平台”。
官方实现流程
MCP 当前使用机制说明
MCP(Model Context Protocol)官方支持多种服务端实现方式:JavaScript 、 Python等。也就是说,开发者可以用这些语言编写符合 MCP 协议的工具服务(MCP Server)。
用户使用流程(Claude / Cursor)
- 用户通过 Claude Desktop(官方桌面对话应用)或 Cursor(支持 Claude 的 AI 编程 IDE)使用 Claude。
- 在应用内部配置一个名为
mcp_config.json
的文件,并填写个人的 Claude API Key。 - 一旦配置完成,Claude 即可拥有访问 MCP 平台上所有 MCP Server 的权限。
Claude 如何调用工具?
Claude 在收到用户输入后,会:
- 通过大模型解析 用户意图;
- 自动判断是否需要借助外部工具;
- 如果需要,就根据工具描述调用注册在 MCP 平台上的对应 MCP Server;
- 最终将执行结果整合进回复中返回给用户。
示例
前提:claude 用的 API 库是 anthropic,这个库只兼容 claude 系列模型。可以用 ollama 或者openai 等SDK去调用本地 ollama URL 以获取模型列表。目前通过 ollama 库去获得模型列表,提取模型输出来决定是否调用 function call。
代码实现
客户端代码实现
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from ollama import chat
class MCPClient:
def __init__(self, action='open'):
self.sessions: list[ClientSession] = []
self.action = action
self.exit_stack = AsyncExitStack()
async def connect_to_server(self, server_script_paths: list[str]):
for server_script_path in server_script_paths:
is_python = server_script_path.endswith('.py')
is_js = server_script_path.endswith('.js')
if not (is_python or is_js):
raise ValueError("Server script must be a .py or .js file")
command = "python" if is_python else "node"
server_params = StdioServerParameters(
command=command,
args=[server_script_path],
env=None
)
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
stdio, write = stdio_transport
session = await self.exit_stack.enter_async_context(ClientSession(stdio, write))
await session.initialize()
self.sessions.append(session)
response = await session.list_tools()
tools = response.tools
print("\nConnected to server with tools:", [tool.name for tool in tools])
async def process_query(self, query: str) -> str:
messages = [
{"role": "user", "content": query},
{"role": "system", "content": "你是一个问答助手,正常回答用户问题。"}
]
all_tools = []
for session in self.sessions:
response = await session.list_tools()
all_tools.extend([
{
"type": "function",
"function": {
"name": tool.name,
"discription": tool.description,
}
} for tool in response.tools
])
print("available_tools:", all_tools)
response = chat(
model='qwen3:0.6b',
messages=messages,
tools=all_tools,
)
final_text = []
assistant_message_content = []
print("response:", response)
print("response.message:", response.message)
print("response.message.content:", response.message.content)
if response.message.content:
final_text.append(response.message.content)
assistant_message_content.append(response.message)
elif response.message.tool_calls:
for tool_call in response.message.tool_calls:
tool_name = tool_call.function.name
tool_args = tool_call.function.arguments
print("tool_name:", tool_name)
for session in self.sessions:
try:
result = await session.call_tool(tool_name, tool_args)
final_text.append(f"[Calling tool {tool_name} with args {tool_args} => {result}]")
break
except Exception:
continue
return "\n".join(final_text)
async def chat_loop(self):
print("\nMCP Client Started!")
print("Type your queries or 'quit' to exit.")
while True:
try:
query = input("\nQuery: ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query)
print("\n" + response)
except Exception as e:
print(f"\nError: {str(e)}")
async def cleanup(self):
await self.exit_stack.aclose()
async def main():
import sys
if len(sys.argv) < 2:
print("Usage: python client.py <path_to_server_script1> [path_to_server_script2 ...]")
sys.exit(1)
script_paths = sys.argv[1:]
client = MCPClient()
try:
await client.connect_to_server(script_paths)
await client.chat_loop()
finally:
await client.cleanup()
if __name__ == "__main__":
asyncio.run(main())
简易计算器服务器实现
from mcp.server.fastmcp import FastMCP
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
mcp = FastMCP("Math")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
logger.info(f"Adding {a} and {b}")
return a + b
@mcp.tool()
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
logger.info(f"Multiplying {a} and {b}")
return a * b
if __name__ == "__main__":
logger.info("Starting Math MCP service...")
mcp.run(transport="stdio")
简易开关灯服务器实现
from mcp.server.fastmcp import FastMCP
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
mcp = FastMCP("Math")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
logger.info(f"Adding {a} and {b}")
return a + b
@mcp.tool()
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
logger.info(f"Multiplying {a} and {b}")
return a * b
if __name__ == "__main__":
logger.info("Starting Math MCP service...")
mcp.run(transport="stdio")
复制代码就能跑,如果不知道怎么跑可以看看我代码仓库的README文件。
点个赞呗,谢谢~