当前位置: 首页 > news >正文

MCP 协议实战:用 50 行代码给本地大模型接上“工具手“,让 Ollama 也能干 Agent 的活

本文面向:对 MCP 协议有基本了解、希望把它真正接到自己本地模型上的开发者。

全文示例已在 Windows 11 + Python 3.11 + Ollama 0.5+ 环境下完整跑通。

0. 写在前面:为什么写这篇

2026 年了,MCP(Model Context Protocol)已经从 Anthropic 自家的协议,变成了几乎所有主流 AI 工具的"事实标准"——Claude Desktop、Cursor、Windsurf、Cline,乃至大部分国产 IDE 插件,都在原生支持。

但当我在 CSDN、掘金、知乎搜了一圈"MCP 教程"以后,发现一个有意思的现象:

90% 的内容停在"MCP 是什么"和"怎么把官方 demo 跑起来"

真正回答"怎么把 MCP 接到本地模型(Ollama / vLLM)上"的资料几乎没有;

而这恰好是企业内网部署、私有化场景下,最刚需的一件事。

所以这篇文章不再重复 MCP 的概念铺垫,而是直接动手——用一个最简单的例子,把"自己写的工具 → 通过 MCP 协议 → 喂给本地 Ollama 模型"这整条链路打通,全部代码不超过 200 行

1. 三分钟看懂 MCP 在干什么

如果你已经熟悉 MCP,可以跳过本节。

简单来说,MCP 把"模型 ↔ 工具"之间的接线,标准化成了三个角色:

Host(宿主):跑模型的那个程序,比如 Claude Desktop、Cursor、或者下面我们自己写的 Python 脚本。

Client(客户端):Host 内部的一个连接器,负责跟下面的 Server 握手、收发消息。

Server(服务端):暴露工具/资源/Prompt 的进程,比如"查数据库""读文件""调内网接口"。

三者之间走的是 JSON-RPC,传输层既可以是stdio(最常用,本地拉起子进程),也可以是SSE / Streamable HTTP(远程部署)。

MCP 架构示意

理解到这里就够了。MCP 的精髓不是"协议有多复杂",而是它把"插一个工具进来"这件事变成了即插即用——你写一个 MCP Server,全世界支持 MCP 的客户端都能直接用它。

但反过来想——既然 MCP Server 是标准的,那么只要我自己写一个最小的 Host,让它能跟 MCP Server 通信、再把工具转给 Ollama 调用,本地模型就同样能享受这套生态。这就是本文的主线。

2. 环境准备

# Python 端

pip install "mcp[cli]" ollama

# Ollama 端(如果还没装)

# Windows: https://ollama.com/download

# 拉一个支持 tool calling 的模型,体积友好的可以选 qwen3:8b

ollama pull qwen3:8b

ollama serve

需要注意两点:

模型必须支持 tool calling。Qwen3、Llama 3.1+、Mistral、DeepSeek-V3 等都已经支持。早期模型(如 Llama 2、Qwen1.5)不支持,硬接会得到一堆乱七八糟的输出。

Ollama 版本 ≥ 0.4.x才有完善的 tool 字段,建议升到最新版。

3. 第一步:写一个最小的 MCP Server(≈ 50 行)

为了让 demo 有"业务感",我们假装自己是某个企业内部场景,需要两个工具:

1.query_orders— 按客户 ID 查最近订单

2.search_docs— 在本地知识库里做一个简单的关键词检索

新建my_mcp_server.py

import asyncio

from mcp.server import Server

from mcp.server.stdio import stdio_server

from mcp.types import Tool, TextContent

# —— 假数据:真实场景这里就是连数据库、调内部 API ——

ORDERS = {

"C001": ["2026-05-12 ¥899 蓝牙耳机", "2026-05-18 ¥2399 显示器"],

"C002": ["2026-05-09 ¥159 USB-C 扩展坞"],

}

DOCS = {

"退货政策": "签收 7 天内可无理由退货,需保持原包装完整。",

"发票申请": "登录会员中心 > 我的订单 > 申请发票,3 个工作日内开具。",

"保修说明": "标配 1 年保修,主板/电池半年保修,人为损坏不在保修范围。",

}

server = Server("demo-tools")

@server.list_tools()

async def list_tools():

return [

Tool(

name="query_orders",

description="按客户 ID 查询最近订单。输入参数:customer_id(字符串)",

inputSchema={

"type": "object",

"properties": {"customer_id": {"type": "string"}},

"required": ["customer_id"],

},

),

Tool(

name="search_docs",

description="在企业知识库中按关键词检索条目。输入参数:keyword(字符串)",

inputSchema={

"type": "object",

"properties": {"keyword": {"type": "string"}},

"required": ["keyword"],

},

),

]

@server.call_tool()

async def call_tool(name: str, arguments: dict):

if name == "query_orders":

cid = arguments.get("customer_id", "")

rows = ORDERS.get(cid)

text = "未查到该客户订单" if not rows else ";".join(rows)

return [TextContent(type="text", text=text)]

if name == "search_docs":

kw = arguments.get("keyword", "")

hits = [f"【{k}】{v}" for k, v in DOCS.items() if kw in k or kw in v]

text = "未命中条目" if not hits else "\n".join(hits)

return [TextContent(type="text", text=text)]

return [TextContent(type="text", text=f"未知工具: {name}")]

async def main():

async with stdio_server() as (read, write):

await server.run(read, write, server.create_initialization_options())

if __name__ == "__main__":

asyncio.run(main())

这个 Server 现在已经是一个"标准 MCP Server",所有支持 MCP 的客户端都能拿来即用

4. 第二步:先用 Claude Desktop / Cursor 验证一下

在写自己的 Host 之前,强烈建议先用现成客户端测一下,确保 Server 本身没问题。

以 Claude Desktop 为例,编辑配置文件:

Windows:%APPDATA%\Claude\claude_desktop_config.json

macOS:~/Library/Application Support/Claude/claude_desktop_config.json

加入:

{"mcpServers": {

"demo-tools": {

"command": "python",

"args": ["D:/your/path/my_mcp_server.py"]

}

}

}

重启 Claude Desktop,在对话框里你应该能看到demo-tools已经被加载,工具图标里多了query_orderssearch_docs

试着问:"客户 C001 最近买了什么?"

如果你能看到 Claude 主动调用query_orders,并把订单内容回出来——恭喜,Server 已经验证通过。

5. 关键章节:让 Ollama 也能用这套 MCP Server

到这里为止,所有教程都讲过。真正的难点是:怎么让本地跑的 Ollama 模型用上这个 Server?

思路其实只有一句话——

我们自己写一个最小的"Host",它一手连 MCP Server(说协议方言),一手连 Ollama(说 OpenAI 兼容的 tool calling 方言),中间做翻译。

新建local_host.py

import asyncio, json

import ollama

from mcp import ClientSession, StdioServerParameters

from mcp.client.stdio import stdio_client

MODEL = "qwen3:8b" # 任何支持 tool calling 的 Ollama 模型

SERVER_CMD = "python"

SERVER_ARGS = ["my_mcp_server.py"]

def mcp_tool_to_ollama(t):

"""把 MCP 协议里的 Tool 对象,翻成 Ollama / OpenAI 兼容的 tool schema。"""

return {

"type": "function",

"function": {

"name": t.name,

"description": t.description,

"parameters": t.inputSchema,

},

}

async def chat_loop(user_query: str):

params = StdioServerParameters(command=SERVER_CMD, args=SERVER_ARGS)

async with stdio_client(params) as (read, write):

async with ClientSession(read, write) as session:

await session.initialize()

# 1) 拿到 MCP Server 暴露的所有工具

tools_resp = await session.list_tools()

ollama_tools = [mcp_tool_to_ollama(t) for t in tools_resp.tools]

messages = [

{"role": "system",

"content": "你是企业客服助手,需要时调用工具查询订单和知识库。"},

{"role": "user", "content": user_query},

]

# 2) 让 Ollama 决定要不要调用工具(最多 5 轮,防止死循环)

for _ in range(5):

resp = ollama.chat(model=MODEL, messages=messages, tools=ollama_tools)

msg = resp["message"]

messages.append(msg)

tool_calls = msg.get("tool_calls") or []

if not tool_calls:

print("\n[最终回答]\n", msg["content"])

return

# 3) 模型决定调用工具 → 走 MCP 真实执行

for call in tool_calls:

fname = call["function"]["name"]

fargs = call["function"]["arguments"]

if isinstance(fargs, str):

fargs = json.loads(fargs)

print(f"[模型调用工具] {fname}({fargs})")

result = await session.call_tool(fname, fargs)

text = result.content[0].text if result.content else ""

print(f"[工具返回] {text}\n")

messages.append({

"role": "tool",

"content": text,

})

if __name__ == "__main__":

asyncio.run(chat_loop("客户 C001 最近买了什么?另外帮我查下退货政策。"))

这就是全部"翻译层"代码。不到 50 行,就把一个标准 MCP Server 接到了本地 Ollama 模型上。

6. 跑通效果

# 终端 1:确保 Ollama 在运行

ollama serve

# 终端 2

python local_host.py

预期输出大概是这样:

[模型调用工具] query_orders({'customer_id': 'C001'})

[工具返回] 2026-05-12 ¥899 蓝牙耳机;2026-05-18 ¥2399 显示器

[模型调用工具] search_docs({'keyword': '退货'})

[工具返回] 【退货政策】签收 7 天内可无理由退货,需保持原包装完整。

[最终回答]

客户 C001 最近购买记录:

1)2026-05-12,蓝牙耳机,¥899

2)2026-05-18,显示器,¥2399

退货政策:自签收之日起 7 天内可无理由退货,需保持原包装完整。

注意三点细节:

模型自己判断了"这个问题里有两件事,要分别调两个工具";

两次 tool 调用都真实走了 MCP 协议,而不是模型自己幻觉编造数据;

整套流程完全跑在本地,没有任何一次外部 API 调用——这正是企业内网场景最需要的形态。

7. 踩坑清单(建议收藏)

下面这些坑,是我把这套链路跑通过程中真实踩到的,按出现频率排序:

1)模型不调用工具,直接瞎回答。

99% 的情况是模型没选对。Qwen3、Llama 3.1+、DeepSeek-V3 这类才行;Llama 2 / 早期 Qwen 不支持。

另一个原因是 system prompt 太软,把"需要时调用工具"改成"必须先调用工具再回答"会立竿见影。

2)`stdio_client` 启动报 "Failed to spawn"。

SERVER_ARGS里的脚本路径必须 Host 那边能解析到。直接写绝对路径最稳。

Windows 下 Python 不在 PATH 时,把SERVER_CMD = "python"改成 Python 绝对路径。

3)`tool_calls` 字段一直为空。

检查 Ollama 版本,0.3.x 之前的版本对 tool 字段支持不全。

也可以打印resp看完整结构——有些模型会把工具调用塞到content里的 JSON 字符串,需要兼容解析。

4)中文输入参数被截断或乱码。

MCP 走 JSON-RPC,本身是 UTF-8。问题通常出在 Windows 终端编码。

进入脚本前执行chcp 65001,或在 Python 启动前设置PYTHONIOENCODING=utf-8

5)多工具并行调用顺序乱。

上面的 demo 是顺序执行,生产环境如果模型一次返回多个 tool call,记得用asyncio.gather并发跑,但要小心写库类工具的并发安全。

8. 接下来还可以怎么扩展

这套最小骨架跑通以后,往下走的路非常宽:

把 stdio 换成 SSE / Streamable HTTP:MCP Server 部署到内网服务器,多台机器共用一个工具集。

把 Ollama 换成 vLLM / SGLang:吞吐量和并发上一个台阶,对外提供企业级 Agent 服务。

接入更多 Server:MCP 生态目前已经有几百个开源 Server——文件系统、Git、PostgreSQL、Slack、Notion……都是开箱即用的,你只需要在 Host 里同时连接多个ClientSession即可。

加上权限控制:MCP 协议本身没规定鉴权,企业场景需要自己在 Host 层加一层"哪个用户能调哪个工具"的策略。

结合本地 RAG:让 MCP 工具暴露知识库检索能力,再叠加长上下文,本地模型就能搞定 80% 的客服/内部助手类需求。

9. 结语

回到开头那句话——

MCP 的真正价值,不是给 Claude 用的,是给"所有模型"用的。

在 2026 年这个节点,谁能率先把"本地大模型 + MCP + 自有工具"这条链路在自己业务里跑通,谁就站在了私有化 Agent 的入场口上。

而入场的代价,看完这篇你应该知道了——

不到 200 行 Python。

文中所有代码均原创编写,已在本地环境跑通。如果你按文章操作时遇到坑,欢迎评论区留版本号和报错信息,我会在置顶补充。

后续会写:《MCP Server 走 SSE 远程部署 + 鉴权方案》《把 MCP 接入 vLLM 提供企业级 Agent 服务》。感兴趣可以先关注。

http://www.gsyq.cn/news/1344028.html

相关文章:

  • 桐乡汽车贴膜哪家好?口碑专业靠谱贴膜门店推荐(2026 本地实用指南) - GrowthUME
  • Windows右键菜单终极优化指南:用ContextMenuManager让你的右键菜单秒开如飞
  • 终极指南:5分钟让Switch手柄在Windows上完美运行
  • Unity IL2CPP运行时调试:Frida-il2cpp-bridge实战指南
  • Unity SpriteShape地形系统:零代码画线生成可编程2D地形
  • Apifox压测功能如何替代JMeter实现高效接口性能测试
  • JMeter+Prometheus构建AI推理压测体系
  • 如何快速掌握拯救者工具箱:联想笔记本性能调校终极指南
  • SQL注入原理与sqlmap实战:从手工验证到自动化渗透
  • sqlmap深度原理与实战调优:从靶场到真实环境的注入审计指南
  • 如何用Seraphine英雄联盟辅助工具在5分钟内提升你的排位赛胜率
  • 基于平行素数对等腰梯形网格拓扑的完备性证明哥德巴赫猜想1+1
  • Unity沙漠场景模块化开发:参数化装配与空间语法构建
  • UE5 BaseInput.ini深度解析:输入配置的底层原理与跨平台实践
  • 【Midjourney新拟态风格实战指南】:20年AI视觉专家亲授7大参数调优公式与3类商业级提示词模板
  • Unity沙漠场景模块化开发:高效拼装与PBR一致性实践
  • 深入理解Android中startActivity的完整流程:聚焦IPC机制与Binder原理
  • 如何快速掌握DLSS Swapper:游戏性能优化终极指南
  • 如何快速掌握猫抓工具:终极视频嗅探与下载指南
  • 深聊专业的中老年婚姻介绍所如何选择,这几点要牢记 - myqiye
  • Unity翻书效果实现原理:顶点着色器级纸张物理建模
  • JMeter分布式压测实战:突破单机瓶颈的原理与落地
  • Markdown图文教程转PPT实战指南
  • 思科:速修复满分 Secure Workload 未授权 API 访问漏洞
  • 3步解锁百度网盘全速下载:告别限速困扰的实用指南
  • 2026最新诚信优选 汕头市澄海区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • iDRAC连接失败根因分析与自动化自愈实践
  • IDRAC连接失败的七层排障指南:从物理层到浏览器层
  • 适合行政小伙伴日常会议整理的,好用会议纪要
  • 3PEAK思瑞浦 LM2902A-SR SOP14 运算放大器