news 2026/8/26 11:31:59

从零手搓MCP Server:深入理解AI工具扩展协议与Python实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零手搓MCP Server:深入理解AI工具扩展协议与Python实战

1. 从“调API”到“造轮子”:为什么我们需要亲手实现一个MCP Server?

如果你最近在AI应用开发领域,尤其是围绕Claude、Cursor这类智能编码工具,那么“MCP”这个词一定高频地出现在你的视野里。Model Context Protocol,这个由Anthropic提出的协议,正在迅速成为连接大模型与外部工具、数据源的事实标准。市面上已经涌现了成百上千个MCP Server,从搜索、文件读写到数据库操作,几乎无所不包。那么问题来了:既然有这么多现成的轮子,为什么我还要费劲“从零手搓”一个MCP Server呢?

这恰恰是“工程化”思维与“调包侠”思维的分水岭。直接调用现成的MCP Server,就像使用一个封装好的黑盒函数,你只知道输入和输出,对其内部的工作机制、性能瓶颈、安全边界一无所知。当工具的行为不符合预期,或者你需要一个高度定制化的功能时,这种“拿来主义”就会让你束手无策。而亲手实现一遍,哪怕是一个最简单的Server,其价值也远超你的想象。这个过程会让你彻底理解MCP协议的核心——它如何定义工具(Tools)、资源(Resources)?Server与Client(比如Claude Desktop)之间是如何通过JSON-RPC进行通信的?消息的序列化、反序列化、错误处理、生命周期管理又是如何运作的?这些底层细节,是你在调用mcp-client库时永远无法触及的。

更重要的是,MCP的定位是“工程化”的粘合剂。一个成熟的AI应用,绝不是简单地问答。它需要能稳定、安全、高效地调用企业内部API、查询专有知识库、执行自动化流程。理解MCP,就是掌握了为AI大模型“装配手脚”和“扩展感官”的核心方法。通过这次实战,我们的目标不是造一个能上生产环境的复杂Server,而是像拆解一台精密的钟表一样,把MCP的每一个齿轮、每一根发条都看清楚,然后自己动手把它们组装起来,让它重新滴答作响。当你完成时,你将获得的不仅是一个可运行的代码,更是一套能够自主设计、调试和优化任何MCP集成方案的“元能力”。

2. 庖丁解牛:拆解MCP协议的核心三要素

在动手写代码之前,我们必须像建筑师看蓝图一样,彻底理解MCP协议的设计哲学。它本质上是一个基于JSON-RPC 2.0的通信协议,其核心思想是将外部能力抽象为两类实体:工具(Tools)资源(Resources),并通过一个标准化的Server暴露给Client(通常是AI助手)。

2.1 工具(Tools):AI的“可调用函数”

工具是MCP中最核心的概念。你可以把它理解为一个函数签名,告诉AI“我能做什么”。每个工具必须明确定义:

  • name: 工具的唯一标识符,例如get_weather
  • description: 工具功能的自然语言描述。这部分至关重要,因为AI主要靠它来决定是否以及如何调用这个工具。描述应清晰、具体,包含输入参数的预期。
  • inputSchema: 定义调用此工具所需的参数,遵循JSON Schema格式。这严格规定了AI必须提供什么样的数据才能成功调用。

例如,一个获取天气的工具定义可能如下所示(在Server初始化时声明):

{ "name": "get_weather", "description": "获取指定城市的当前天气信息。", "inputSchema": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如:Beijing, Shanghai" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位,默认为摄氏度(celsius)" } }, "required": ["city"] } }

当AI(Client)决定使用这个工具时,它会向Server发送一个tools/call请求,其中包含name和匹配inputSchemaarguments。Server收到后执行实际逻辑(如调用第三方天气API),然后将结果通过tools/call响应返回给AI。

注意:工具描述的质量直接决定了AI使用的准确性。模糊的描述会导致AI误用或不用。好的描述应像一份精简的API文档。

2.2 资源(Resources):AI的“可读取文档”

资源代表了Client可以读取的静态或动态内容,比如文件、数据库查询结果、API返回的文本等。与工具不同,资源是“只读”的,AI不能通过资源执行操作,但可以获取其内容来丰富上下文。

资源的关键属性包括:

  • uri: 资源的唯一标识符,格式类似file:///path/to/doc.mddynamic://stock/TSLA
  • mimeType: 内容的媒体类型,如text/plain,text/markdown,application/json
  • namedescription: 便于AI理解资源内容的元数据。

Server通过resources/listresources/read请求来向Client宣告和提供资源内容。例如,一个Server可以声明一个动态资源dynamic://system/time,当AI需要知道当前时间时,Client会请求读取这个资源,Server则实时生成当前时间的文本返回。

2.3 Server与Client:基于JSON-RPC的对话机制

MCP Server是一个独立的进程,通过标准输入输出(stdio)或HTTP等传输层与Client通信。所有的交互都封装在JSON-RPC 2.0的消息中。一个典型的启动和交互流程如下:

  1. 初始化(Initialization): Client启动Server后,首先发送initialize请求。Server回复其支持的协议版本、能力(如支持哪些工具和资源列表)以及一个唯一的serverId
  2. 能力宣告(Capabilities Announcement): 在初始化完成后,Server会主动(或响应Client请求)发送notifications或响应requests,来告知Client当前可用的工具列表(tools/list)和资源列表(resources/list)。
  3. 工具调用(Tool Invocation): AI在对话中判断需要调用工具时,Client会向Server发送tools/call请求。Server执行实际工作,并返回tools/call响应,其中包含结果或错误信息。
  4. 资源读取(Resource Reading): 类似地,Client发送resources/read请求来获取特定URI的资源内容。
  5. 心跳与关闭(Keep-alive & Shutdown): 为了管理连接,协议通常还包含ping/pong或自定义的心跳机制,以及优雅关闭的shutdown请求。

理解了这个通信模型,我们就知道,编写一个MCP Server,主要工作就是:监听特定的JSON-RPC请求,根据请求类型(method)执行对应的业务逻辑,然后返回格式正确的JSON-RPC响应。接下来,我们就用代码将这个蓝图变为现实。

3. 实战第一步:搭建最小可行MCP Server骨架

我们选择使用Python来实现,因为它生态丰富、上手简单,并且有不错的MCP基础库支持。这里我们不直接使用最高抽象的框架(如mcp),而是从更底层的sse-starlettepydantic入手,这样能更清晰地看到每一部分是如何工作的。

3.1 项目初始化与依赖安装

首先,创建一个新的项目目录并初始化虚拟环境,这是保证依赖隔离的好习惯。

mkdir my-first-mcp-server && cd my-first-mcp-server python -m venv venv # 在Windows上使用 `venv\Scripts\activate` source venv/bin/activate

接着,安装核心依赖。我们将使用fastapisse-starlette来方便地处理HTTP和服务器发送事件(Server-Sent Events),这是MCP over HTTP的一种常见传输方式。pydantic用于严谨的数据验证和序列化。

pip install fastapi sse-starlette pydantic

3.2 定义协议数据模型:用Pydantic塑造“坚固的管道”

MCP协议中流动的所有数据——请求、响应、通知——都有严格的结构。使用Pydantic模型来定义它们,相当于为我们的数据管道加上了编译时类型检查,能极大减少低级错误。

我们在models.py文件中定义几个最核心的模型:

from typing import Any, Dict, List, Optional, Union from pydantic import BaseModel, Field # JSON-RPC 2.0 基础请求结构 class JSONRPCRequest(BaseModel): jsonrpc: str = Field("2.0", const=True) id: Optional[Union[int, str]] = None method: str params: Optional[Dict[str, Any]] = None # JSON-RPC 2.0 基础响应/错误结构 class JSONRPCResponse(BaseModel): jsonrpc: str = Field("2.0", const=True) id: Optional[Union[int, str]] = None result: Optional[Any] = None error: Optional[Dict[str, Any]] = None # MCP 工具定义 class Tool(BaseModel): name: str description: str inputSchema: Dict[str, Any] # JSON Schema # MCP 资源定义 class Resource(BaseModel): uri: str name: str description: Optional[str] = None mimeType: str = "text/plain" # MCP 初始化参数 class InitializeParams(BaseModel): protocolVersion: str = "2024-11-05" clientInfo: Optional[Dict[str, str]] = None # MCP 工具调用参数 class CallToolParams(BaseModel): name: str arguments: Optional[Dict[str, Any]] = None # MCP 读取资源参数 class ReadResourceParams(BaseModel): uri: str

这些模型就像乐高积木的模具,确保了我们将要组装和传递的每一个数据块都是形状正确的。

3.3 实现Server核心循环:处理请求的路由器

现在,我们来创建Server的主文件server.py。它的核心是一个能根据JSON-RPC请求的method字段,将请求路由到对应处理函数的路由器。

from fastapi import FastAPI, Request from sse_starlette.sse import EventSourceResponse import asyncio import json from models import * app = FastAPI() # 存储当前可用的工具和资源 available_tools: List[Tool] = [] available_resources: List[Resource] = [] async def handle_jsonrpc_request(request_data: Dict) -> Dict: """核心请求处理器""" try: req = JSONRPCRequest(**request_data) except Exception as e: # 如果连基础请求结构都不对,返回解析错误 return JSONRPCResponse( id=None, error={"code": -32700, "message": "Parse error", "data": str(e)} ).dict() handler_map = { "initialize": handle_initialize, "tools/list": handle_tools_list, "tools/call": handle_tools_call, "resources/list": handle_resources_list, "resources/read": handle_resources_read, # 可以继续添加其他method的处理函数 } handler = handler_map.get(req.method) if not handler: return JSONRPCResponse( id=req.id, error={"code": -32601, "message": f"Method not found: {req.method}"} ).dict() try: result = await handler(req.params, req.id) # 处理函数应返回一个符合JSONRPCResponse结构的字典 return result except Exception as e: # 处理函数内部出错 return JSONRPCResponse( id=req.id, error={"code": -32603, "message": "Internal error", "data": str(e)} ).dict() # 定义各个请求的处理函数(暂时留空) async def handle_initialize(params: Optional[Dict], request_id: Any) -> Dict: pass async def handle_tools_list(params: Optional[Dict], request_id: Any) -> Dict: pass async def handle_tools_call(params: Optional[Dict], request_id: Any) -> Dict: pass async def handle_resources_list(params: Optional[Dict], request_id: Any) -> Dict: pass async def handle_resources_read(params: Optional[Dict], request_id: Any) -> Dict: pass # HTTP端点:用于接收Client的POST请求(标准JSON-RPC over HTTP) @app.post("/jsonrpc") async def jsonrpc_endpoint(request: Request): body = await request.json() response = await handle_jsonrpc_request(body) return response # SSE端点:用于Client建立长连接,Server可以主动推送通知(如工具列表更新) @app.get("/sse") async def sse_endpoint(request: Request): async def event_generator(): # 这里可以维护一个连接队列,当有通知时推送给所有连接的Client # 例如,当工具列表更新时,发送一个 `tools/list` 通知 # 为了简单,我们先返回一个空的事件流 while True: if await request.is_disconnected(): break # 可以在这里检查是否有需要推送的通知 # 暂时只发送一个保持连接的心跳注释 yield {"event": "comment", "data": "heartbeat"} await asyncio.sleep(30) # 30秒心跳 return EventSourceResponse(event_generator())

这个骨架搭建了起来。它有一个HTTP端点/jsonrpc来接收请求,一个SSE端点/sse用于服务器主动推送。handle_jsonrpc_request函数是大脑,负责解析请求并分发给对应的处理函数。现在,我们需要为这些处理函数注入灵魂。

4. 注入灵魂:实现核心工具与资源处理器

骨架已经搭好,现在需要实现具体的业务逻辑。我们来实现两个最经典的功能:一个计算器工具和一个动态时间资源

4.1 实现初始化与列表查询

首先,在Server启动时,我们需要注册我们的工具和资源。修改server.py的顶部和初始化函数:

# 在文件顶部,定义我们的工具和资源 available_tools = [ Tool( name="calculate", description="执行简单的数学计算。支持加(+)、减(-)、乘(*)、除(/)。", inputSchema={ "type": "object", "properties": { "expression": { "type": "string", "description": "数学表达式,例如:'3 + 5 * 2'。注意:乘号是*,除号是/。" } }, "required": ["expression"] } ) ] available_resources = [ Resource( uri="dynamic://server/current_time", name="当前服务器时间", description="获取服务器当前的日期和时间。", mimeType="text/plain" ) ] async def handle_initialize(params: Optional[Dict], request_id: Any) -> Dict: """处理初始化请求""" init_params = InitializeParams(**(params or {})) # 这里可以检查protocolVersion是否兼容 response = JSONRPCResponse( id=request_id, result={ "protocolVersion": init_params.protocolVersion, "capabilities": { "tools": {"listChanged": True}, # 告知Client工具列表可能会变 "resources": {"listChanged": True} # 告知Client资源列表可能会变 }, "serverInfo": { "name": "My First MCP Server", "version": "0.1.0" } } ) return response.dict() async def handle_tools_list(params: Optional[Dict], request_id: Any) -> Dict: """返回当前可用的工具列表""" response = JSONRPCResponse( id=request_id, result={"tools": [tool.dict() for tool in available_tools]} ) return response.dict() async def handle_resources_list(params: Optional[Dict], request_id: Any) -> Dict: """返回当前可用的资源列表""" response = JSONRPCResponse( id=request_id, result={"resources": [resource.dict() for resource in available_resources]} ) return response.dict()

初始化处理函数handle_initialize向Client宣告了Server的基本信息和能力。tools/listresources/list处理函数则直接返回我们预定义好的列表。注意capabilities中的listChanged字段设为True,这意味着我们的Server支持在运行时动态更新列表,并会通过SSE通道发送通知(本例暂未实现动态更新)。

4.2 实现计算器工具调用

这是核心中的核心。当AI发送tools/call请求,要求调用calculate工具时,我们需要:

  1. 验证参数是否符合schema
  2. 安全地执行计算表达式(这是重点和难点!)。
  3. 返回结果或错误。
import ast import operator # 安全评估表达式的辅助函数 def safe_eval_expression(expr: str): """ 极其有限且安全地评估一个只包含数字和基础运算符的字符串表达式。 警告:绝对不要在生产环境中用eval()直接执行用户输入! 这里使用ast.literal_eval也有局限,我们实现一个简单的解析器作为示例。 """ # 一个非常简陋的、仅用于演示的解析器:按空格分割,假设是逆波兰表达式或简单二元运算 # 例如:只处理 "a op b" 形式,如 "3 + 5" tokens = expr.split() if len(tokens) != 3: raise ValueError("表达式格式暂只支持 '数字 运算符 数字',如 '3 + 5'") try: a = float(tokens[0]) b = float(tokens[2]) except ValueError: raise ValueError("操作数必须是数字") op_map = { '+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv } op_func = op_map.get(tokens[1]) if not op_func: raise ValueError(f"不支持的运算符: {tokens[1]}。支持: +, -, *, /") if tokens[1] == '/' and b == 0: raise ZeroDivisionError("除数不能为零") result = op_func(a, b) # 如果是整数,返回整数形式 if result.is_integer(): return int(result) return result async def handle_tools_call(params: Optional[Dict], request_id: Any) -> Dict: """处理工具调用请求""" if not params: return JSONRPCResponse( id=request_id, error={"code": -32602, "message": "Invalid params"} ).dict() call_params = CallToolParams(**params) if call_params.name == "calculate": # 1. 获取参数 arguments = call_params.arguments or {} expression = arguments.get("expression") if not expression: return JSONRPCResponse( id=request_id, error={"code": -32602, "message": "Missing required argument: 'expression'"} ).dict() # 2. 执行计算(在真实场景中,这里需要更复杂和安全的方法) try: # 注意:这里使用自定义的安全函数,而非eval result_value = safe_eval_expression(expression) except ZeroDivisionError: return JSONRPCResponse( id=request_id, error={"code": -32000, "message": "Calculation error", "data": "Division by zero."} ).dict() except Exception as e: return JSONRPCResponse( id=request_id, error={"code": -32000, "message": "Calculation error", "data": str(e)} ).dict() # 3. 返回成功结果 response = JSONRPCResponse( id=request_id, result={ "content": [ { "type": "text", "text": f"表达式 `{expression}` 的计算结果是:{result_value}" } ] } ) return response.dict() # 如果工具名未找到 return JSONRPCResponse( id=request_id, error={"code": -32601, "message": f"Tool not found: {call_params.name}"} ).dict()

这里有一个至关重要的安全警告:在真实的生产环境中,绝对禁止使用Python内置的eval()函数来执行用户(AI)提供的表达式字符串,这会带来严重的代码注入安全风险。上面的safe_eval_expression是一个极度简化的示例,仅用于演示原理。在实际项目中,你需要使用更安全的数学表达式解析库(如asteval,它利用AST进行有限制评估),或者将计算任务委托给一个严格沙箱化的环境。

4.3 实现动态时间资源读取

资源读取的逻辑相对简单,主要是根据请求的URI,生成或获取对应的内容。

from datetime import datetime async def handle_resources_read(params: Optional[Dict], request_id: Any) -> Dict: """处理资源读取请求""" if not params: return JSONRPCResponse( id=request_id, error={"code": -32602, "message": "Invalid params"} ).dict() read_params = ReadResourceParams(**params) if read_params.uri == "dynamic://server/current_time": # 动态生成当前时间 current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") content_text = f"当前服务器时间是:{current_time}" response = JSONRPCResponse( id=request_id, result={ "contents": [ { "uri": read_params.uri, "mimeType": "text/plain", "text": content_text } ] } ) return response.dict() # 如果资源URI未找到 return JSONRPCResponse( id=request_id, error={"code": -32601, "message": f"Resource not found: {read_params.uri}"} ).dict()

至此,一个具备最小功能集的MCP Server就实现了。它能够响应初始化、列表查询、工具调用和资源读取请求。你可以使用uvicorn来运行它:

uvicorn server:app --reload --port 8000

Server将在http://localhost:8000启动。接下来,我们需要一个Client来测试它。

5. 验证与调试:打造一个简易的MCP Client测试器

为了验证我们的Server是否正常工作,我们不能只依赖Claude Desktop。自己写一个简单的测试Client是理解和调试MCP协议的最佳方式。这个Client会模拟标准MCP Client(如Claude)的行为,向我们的Server发送请求并打印响应。

创建test_client.py

import asyncio import aiohttp import json async def test_mcp_server(): server_url = "http://localhost:8000" async with aiohttp.ClientSession() as session: print("1. 发送初始化请求...") init_request = { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "clientInfo": {"name": "TestClient"} } } async with session.post(f"{server_url}/jsonrpc", json=init_request) as resp: init_result = await resp.json() print(f"初始化响应: {json.dumps(init_result, indent=2, ensure_ascii=False)}") print("\n2. 查询工具列表...") tools_list_request = { "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} } async with session.post(f"{server_url}/jsonrpc", json=tools_list_request) as resp: tools_result = await resp.json() print(f"工具列表: {json.dumps(tools_result, indent=2, ensure_ascii=False)}") print("\n3. 调用计算器工具...") call_tool_request = { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "calculate", "arguments": { "expression": "10 * 2 + 3" } } } async with session.post(f"{server_url}/jsonrpc", json=call_tool_request) as resp: call_result = await resp.json() print(f"工具调用结果: {json.dumps(call_result, indent=2, ensure_ascii=False)}") print("\n4. 查询资源列表...") resources_list_request = { "jsonrpc": "2.0", "id": 4, "method": "resources/list", "params": {} } async with session.post(f"{server_url}/jsonrpc", json=resources_list_request) as resp: resources_result = await resp.json() print(f"资源列表: {json.dumps(resources_result, indent=2, ensure_ascii=False)}") print("\n5. 读取时间资源...") read_resource_request = { "jsonrpc": "2.0", "id": 5, "method": "resources/read", "params": { "uri": "dynamic://server/current_time" } } async with session.post(f"{server_url}/jsonrpc", json=read_resource_request) as resp: read_result = await resp.json() print(f"资源内容: {json.dumps(read_result, indent=2, ensure_ascii=False)}") if __name__ == "__main__": asyncio.run(test_mcp_server())

运行这个测试脚本(确保Server已在运行):

python test_client.py

你应该能看到一系列格式规范的JSON响应。如果一切顺利,在调用calculate工具时,你会收到一个包含计算结果文本的响应;在读取时间资源时,会得到当前的服务器时间。这个过程让你清晰地看到了MCP协议下“请求-响应”的完整数据流。

6. 进阶与生产化思考:从玩具到工具

我们的“手搓”Server已经能跑通了,但它离一个健壮、可用的生产级MCP Server还有巨大差距。这一步的思考,才是“工程化”的真正开始。

6.1 传输层与部署:不止于HTTP

我们使用了HTTP+SSE,这是MCP的一种传输方式(transport: sse)。但MCP官方还支持标准的stdio(标准输入输出),这对于与Claude Desktop等本地应用集成更为常见。这意味着你的Server需要能够从sys.stdin读取JSON-RPC请求,并将响应写入sys.stdout。你需要修改Server的启动入口,根据环境变量(如MCP_TRANSPORT)来决定使用哪种传输方式。对于生产部署,你可能会将Server打包成Docker容器,并通过stdio与宿主机上的AI助手通信。

6.2 连接管理与状态维护

我们的示例Server是无状态的,每次请求都是独立的。但在真实场景中:

  • 会话(Session):Client和Server之间可能维持一个会话,包含一些上下文信息。
  • 资源订阅(Resource Subscription):Client可以订阅某个资源,当其内容变化时,Server需要通过SSE主动推送更新通知(notifications/resources/updated)。这要求Server维护资源的状态和Client的订阅列表。
  • 工具列表动态更新:同样,当Server安装或移除了一个工具,需要广播notifications/tools/list_changed通知。

6.3 安全性、错误处理与日志

  • 输入验证与消毒:我们强调了计算器工具的安全问题,这只是一个缩影。所有来自AI的输入都必须视为不可信的,需要进行严格的验证和消毒,防止注入攻击。
  • 身份验证与授权:如果你的Server连接了内部数据库或敏感API,那么必须实现身份验证。MCP协议本身不规定认证方式,这需要你在传输层(如HTTP头添加API Key)或应用层自行实现。
  • 全面的错误处理:我们的示例只有基础错误。一个健壮的Server需要对各种边界情况(网络超时、第三方API失败、无效参数组合等)定义清晰的错误码和友好的错误信息,并通过JSON-RPC的error字段返回。
  • 结构化日志:为了方便运维和调试,所有重要的操作(收到请求、调用工具、发生错误)都应该被记录,并包含请求ID、工具名、耗时等上下文信息。

6.4 性能与可观测性

  • 异步与并发:使用asyncio(如我们所用)或其它异步框架来处理并发请求,避免阻塞。
  • 指标(Metrics):暴露Prometheus格式的指标端点,监控请求量、延迟、错误率。
  • 跟踪(Tracing):集成OpenTelemetry,追踪一个用户请求从AI发出,经过MCP Server,调用下游服务,再返回的完整链路。

7. 整合到真实环境:在Claude Desktop中连接你的Server

最后,让我们把亲手打造的Server用起来。以Claude Desktop为例,你需要编辑其配置文件来添加我们的自定义Server。

在macOS上,配置文件通常位于~/Library/Application Support/Claude/claude_desktop_config.json。在Windows上,位于%APPDATA%\Claude\claude_desktop_config.json

你需要添加一个mcpServers配置项。由于我们的Server使用HTTP,配置可能如下所示(注意:Claude Desktop默认更倾向于stdio,对SSE的支持可能需要特定版本或配置):

{ "mcpServers": { "my-calculator-server": { "command": "uvicorn", "args": [ "server:app", "--host", "0.0.0.0", "--port", "8000" ], "env": { "PYTHONPATH": "/path/to/your/project" } // 或者,如果你的Server已经作为常驻进程运行,可以配置为使用SSE传输 // "url": "http://localhost:8000/sse", // "transport": "sse" } } }

更常见的做法是让Server支持stdio传输。这意味着你需要修改Server,使其能够从命令行启动,并通过标准输入输出进行通信。这通常涉及解析sys.argv,监听sys.stdin的输入,并写入sys.stdout。许多MCP SDK(如Python的mcp库)已经帮你处理了这部分样板代码。

配置完成后,重启Claude Desktop。在对话中,你应该能看到Claude已经识别到了新的工具。你可以尝试对它说:“请用我的计算器工具计算一下(15 - 3) * 4 的结果。” 如果一切配置正确,Claude会调用你的Server并返回计算结果。

这个过程可能会遇到各种问题:环境变量路径不对、端口冲突、协议版本不匹配、Claude Desktop缓存了旧的工具列表等等。调试的关键在于查看Claude Desktop的日志(通常可以在其设置中找到日志文件路径),以及确保你的Server日志是详细且可读的。这就是为什么前面强调日志和错误处理的重要性——当集成出现问题时,清晰的日志是你唯一的救生索。

从亲手解析第一个JSON-RPC请求,到安全地实现一个工具,再到最终与AI助手成功联动,这个完整的闭环体验,就是理解MCP工程化精髓的最佳路径。你不再只是一个API的调用者,而是成为了扩展AI能力边界的构建者。下次当你再看到Tavily Search MCP、Brave Search MCP这些复杂的Server时,你看到的将不再是一个黑盒,而是一个个由类似我们今天搭建的骨架,填充了不同业务逻辑后形成的、有生命力的服务。这就是“彻底搞懂”之后,世界在你眼中呈现出的不同模样。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 11:26:26

Windows权限提升攻防:溢出漏洞与土豆家族技术深度解析

1. 项目概述:Windows权限提升的攻防博弈场在Windows安全领域,权限提升(Privilege Escalation)是一个永恒的核心议题。它指的是攻击者或安全测试人员,从一个较低权限的账户(如普通用户、IIS应用程序池账户&a…

作者头像 李华
网站建设 2026/8/26 11:25:28

Python爬虫实战:从飞卢小说网抓取小说并生成离线阅读文件

1. 项目缘起:为什么选择飞卢小说网作为爬取目标? 最近在整理自己的电子书库,想找几本特定题材的小说离线阅读,结果发现很多平台要么需要付费订阅,要么就是阅读体验被广告和弹窗搞得支离破碎。作为一个有十多年经验的开…

作者头像 李华
网站建设 2026/8/26 11:21:05

EPLAN API开发入门:搞清接口、脚本与插件的本质区别

简介:在电气设计自动化领域,EPLAN作为主流的工程规划工具,其二次开发能力越来越受重视。API(应用程序编程接口)本质上是软件对外提供的一组编程调用规范,开发者通过编写代码即可操控项目文件中的底层数据&a…

作者头像 李华
网站建设 2026/8/26 11:20:47

MX25L128/MX25L256 SPI NOR Flash驱动移植与调试实战指南

简介:嵌入式系统中,SPI NOR Flash凭借接口简单、存储可靠等特性,被广泛应用于固件存储、日志记录与OTA升级等场景。其工作原理是通过SPI总线发送指令、地址和数据,完成读、写、擦除操作。掌握驱动移植方法,理解页编程的…

作者头像 李华
网站建设 2026/8/26 11:15:59

人形机器人半马:一场21公里的系统可靠性压力测试

2027年的北京亦庄,可能会迎来一场不设任何实验室滤镜的人形机器人半程马拉松。赛事已经开启全球邀请,规格还在继续升级。但如果只是把它当成一条科技新闻,你会错过这个事件对工程师的真正价值——它本质上是一次把机器人从演示推向长期运行的…

作者头像 李华
网站建设 2026/8/26 11:12:24

工业4.0技术演进与MQTT工业数据采集实战

关于“工业革命是不是当今技术爆发式增长的好先例”这个问题,技术圈的讨论很多。有人从经济学角度看到的是产能跃迁,有人从社会学角度看到的是结构震荡。但如果把问题翻译成工程师的语言——历次技术革命中的基础设施、标准化、平台化规律,能…

作者头像 李华