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

从零开始构建 Ollama + MCP 服务器

Model Context Protocol(模型上下文协议)在过去几个月里已经霸占了大家的视野,出现了许多酷炫的集成示例。我坚信它会成为一种标准,因为它正在定义工具与代理或软件与 AI 模型之间如何集成的新方式。

我决定尝试将 Ollama 中的一个小型 LLM 连接到一个 MCP 服务器上,感受一下这个新标准的魅力。今天,我想向大家展示一种将 Ollama 与 MCP 服务器集成的可能实现方式。
在这里插入图片描述

魔法配方

集成的主要步骤如下:

  1. 创建一个测试用的 MCP 服务器。
  2. 创建一个客户端文件,用于发送请求并启动服务器。
  3. 将服务器的工具获取到客户端。
  4. 将工具转换为 Pydantic 模型。
  5. 通过响应的 format 字段将工具(作为 Pydantic 模型)传递给 Ollama。
  6. 通过 Ollama 发送对话并接收结构化输出。
  7. 如果响应中包含工具,则向服务器发起请求。

安装依赖

要运行这个项目,需要安装所需的包。fastmcp 库在使用 uv 运行代码时表现最佳。它像 Poetry 和 pip 一样,下载方便,使用简单。

使用以下命令将所需的库添加到项目中:

uv add fastmcp ollama

这将安装 MCP 服务器和 Ollama 聊天库,你可以在此基础上构建客户端和服务器逻辑。

文件结构

设置完成后,你的文件夹结构应该如下所示:

your folder
├── server.py
└── client.py

server.py 文件包含 MCP 服务器和你想要暴露的工具。client.py 文件在后台进程启动服务器,获取可用工具,并与 Ollama 连接。

示例 MCP 服务器

让我们从创建一个简单的 MCP 服务器开始,使用 fastmcp 库。服务器暴露了一个名为 magicoutput 的工具。该函数接受两个字符串输入,并返回一个固定的字符串作为输出。

使用 @mcp.tool() 装饰器将函数注册为 MCP 服务器中的可用工具。服务器启动后,任何客户端都可以获取并调用这个工具。

通过在主块中调用 mcp.run() 启动服务器。

# server.py
from fastmcp import FastMCP
# 创建一个 MCP 服务器
mcp = FastMCP("TestServer")
# 我的工具:
@mcp.tool()
def magicoutput(obj1: str, obj2: str) -> int:"""使用这个函数来获取神奇的输出"""return "WomboWombat"
if __name__ == "__main__":mcp.run()

获取服务器工具

为了连接到 MCP 服务器并列出可用工具,我们使用 ClientSessionStdioServerParametersstdio_client,它们都来自 mcp 库。

我们定义了一个名为 OllamaMCP 的类,用于处理服务器连接和工具获取。在类中,_async_run 方法启动一个异步会话,初始化它,并从服务器获取工具列表。

我们使用 threading.Event() 来跟踪会话何时准备就绪,并将工具列表存储在 self.tools 中。

在脚本的末尾,我们定义了服务器参数,并在后台线程中运行客户端。这将启动连接并打印服务器返回的工具元数据。

# client.py
import asyncio
import threading
from pathlib import Path
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from typing import Anyclass OllamaMCP:"""Ollama 和 FastMCP 之间的简单集成"""def __init__(self, server_params: StdioServerParameters):self.server_params = server_paramsself.initialized = threading.Event()self.tools: list[Any] = []def _run_background(self):asyncio.run(self._async_run())async def _async_run(self):try:async with stdio_client(self.server_params) as (read, write):async with ClientSession(read, write) as session:await session.initialize()self.session = sessiontools_result = await session.list_tools()self.tools = tools_result.toolsprint(tools_result)except Exception as e:print(f"初始化 MCP 服务器时出错 {str(e)}")
if __name__ == "__main__":server_parameters = StdioServerParameters(command="uv",args=["run", "python", "server.py"],cwd=str(Path.cwd()))ollamamcp = OllamaMCP(server_params=server_parameters)ollamamcp._run_background()

运行上述代码后,你会从服务器收到以下响应,其中可以看到服务器上可用的工具列表。

[04/14/25 22:29:08] INFO     正在启动服务器 "TestServer"...    server.py:171INFO     正在处理请求类型         server.py:534ListToolsRequest
meta=None nextCursor=None tools=[Tool(name='magicoutput', description='使用这个函数来获取神奇的输出', inputSchema={'properties': {'obj1': {'title': 'Obj1', 'type': 'string'}, 'obj2': {'title': 'Obj2', 'type': 'string'}}, 'required': ['obj1', 'obj2'], 'title': 'magicoutputArguments', 'type': 'object'})]

将工具转换为 Pydantic 模型

现在我们已经从服务器获取了工具列表,下一步是将它们转换为 Pydantic 模型。我们使用 Pydantic 的 create_model 动态定义一个新的响应模式,基于服务器的工具定义。还有一个辅助函数,用于将 JSON 类型映射为有效的 Python 类型。

这可以帮助我们动态定义模型,以便 LLM 精确地知道在返回工具参数时应使用什么结构。

# client.py
import asyncio
import threading
from pathlib import Path
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from typing import Any, Union, Optional
from pydantic import BaseModel, create_model, Fieldclass OllamaMCP:"""Ollama 和 FastMCP 之间的简单集成"""def __init__(self, server_params: StdioServerParameters):self.server_params = server_paramsself.initialized = threading.Event()self.tools: list[Any] = []def _run_background(self):asyncio.run(self._async_run())async def _async_run(self):try:async with stdio_client(self.server_params) as (read, write):async with ClientSession(read, write) as session:await session.initialize()self.session = sessiontools_result = await session.list_tools()self.tools = tools_result.toolsexcept Exception as e:print(f"初始化 MCP 服务器时出错 {str(e)}")def create_response_model(self):dynamic_classes = {}for tool in self.tools:class_name = tool.name.capitalize()properties = {}for prop_name, prop_info in tool.inputSchema.get("properties", {}).items():json_type = prop_info.get("type", "string")properties[prop_name] = self.convert_json_type_to_python_type(json_type)model = create_model(class_name,__base__=BaseModel,__doc__=tool.description,**properties,)dynamic_classes[class_name] = modelif dynamic_classes:all_tools_type = Union[tuple(dynamic_classes.values())]Response = create_model("Response",__base__=BaseModel,__doc__="LLm 响应类",response=(str, Field(..., description= "确认将调用该函数的消息")),tool=(all_tools_type, Field(...,description="用于运行并获取神奇输出的工具")),)else:Response = create_model("Response",__base__=BaseModel,__doc__="LLm 响应类",response=(str, ...),tool=(Optional[Any], Field(None, description="如果不返回 None,则使用此工具")),)self.response_model = Responseprint(Response.model_fields)@staticmethoddef convert_json_type_to_python_type(json_type: str):"""将 JSON 类型简单映射为 Python(Pydantic)类型"""if json_type == "integer":return (int, ...)if json_type == "number":return (float, ...)if json_type == "string":return (str, ...)if json_type == "boolean":return (bool, ...)return (str, ...)# 从零开始构建 Ollama + MCP 服务器Model Context Protocol(模型上下文协议)在过去几个月里已经霸占了大家的视野,出现了许多酷炫的集成示例。我坚信它会成为一种标准,因为它正在定义工具与代理或软件与 AI 模型之间如何集成的新方式。既然我最喜欢写的是小型语言模型(LLMs),我决定尝试将 Ollama 中的一个小型 LLM 连接到一个 MCP 服务器上,感受一下这个新标准的魅力。今天,我想向大家展示一种将 Ollama 与 MCP 服务器集成的可能实现方式。## 魔法配方集成的主要步骤如下:1. 创建一个测试用的 MCP 服务器。
2. 创建一个客户端文件,用于发送请求并启动服务器。
3. 将服务器的工具获取到客户端。
4. 将工具转换为 Pydantic 模型。
5. 通过响应的 `format` 字段将工具(作为 Pydantic 模型)传递给 Ollama。
6. 通过 Ollama 发送对话并接收结构化输出。
7. 如果响应中包含工具,则向服务器发起请求。## 安装依赖要运行这个项目,需要安装所需的包。`fastmcp` 库在使用 `uv` 运行代码时表现最佳。它像 Poetry 和 pip 一样,下载方便,使用简单。使用以下命令将所需的库添加到项目中:```csharp
uv add fastmcp ollama

这将安装 MCP 服务器和 Ollama 聊天库,你可以在此基础上构建客户端和服务器逻辑。

文件结构

设置完成后,你的文件夹结构应该如下所示:

your folder
├── server.py
└── client.py

server.py 文件包含 MCP 服务器和你想要暴露的工具。client.py 文件在后台进程启动服务器,获取可用工具,并与 Ollama 连接。

示例 MCP 服务器

让我们从创建一个简单的 MCP 服务器开始,使用 fastmcp 库。服务器暴露了一个名为 magicoutput 的工具。该函数接受两个字符串输入,并返回一个固定的字符串作为输出。

使用 @mcp.tool() 装饰器将函数注册为 MCP 服务器中的可用工具。服务器启动后,任何客户端都可以获取并调用这个工具。

通过在主块中调用 mcp.run() 启动服务器。

# server.py
from fastmcp import FastMCP
# 创建一个 MCP 服务器
mcp = FastMCP("TestServer")
# 我的工具:
@mcp.tool()
def magicoutput(obj1: str, obj2: str) -> int:"""使用这个函数来获取神奇的输出"""return "WomboWombat"
if __name__ == "__main__":mcp.run()

获取服务器工具

为了连接到 MCP 服务器并列出可用工具,我们使用 ClientSessionStdioServerParametersstdio_client,它们都来自 mcp 库。

我们定义了一个名为 OllamaMCP 的类,用于处理服务器连接和工具获取。在类中,_async_run 方法启动一个异步会话,初始化它,并从服务器获取工具列表。

我们使用 threading.Event() 来跟踪会话何时准备就绪,并将工具列表存储在 self.tools 中。

在脚本的末尾,我们定义了服务器参数,并在后台线程中运行客户端。这将启动连接并打印服务器返回的工具元数据。

# client.py
import asyncio
import threading
from pathlib import Path
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from typing import Anyclass OllamaMCP:"""Ollama 和 FastMCP 之间的简单集成"""def __init__(self, server_params: StdioServerParameters):self.server_params = server_paramsself.initialized = threading.Event()self.tools: list[Any] = []def _run_background(self):asyncio.run(self._async_run())async def _async_run(self):try:async with stdio_client(self.server_params) as (read, write):async with ClientSession(read, write) as session:await session.initialize()self.session = sessiontools_result = await session.list_tools()self.tools = tools_result.toolsprint(tools_result)except Exception as e:print(f"初始化 MCP 服务器时出错 {str(e)}")
if __name__ == "__main__":server_parameters = StdioServerParameters(command="uv",args=["run", "python", "server.py"],cwd=str(Path.cwd()))ollamamcp = OllamaMCP(server_params=server_parameters)ollamamcp._run_background()

运行上述代码后,你会从服务器收到以下响应,其中可以看到服务器上可用的工具列表。

[04/14/25 22:29:08] INFO     正在启动服务器 "TestServer"...    server.py:171INFO     正在处理请求类型         server.py:534ListToolsRequest
meta=None nextCursor=None tools=[Tool(name='magicoutput', description='使用这个函数来获取神奇的输出', inputSchema={'properties': {'obj1': {'title': 'Obj1', 'type': 'string'}, 'obj2': {'title': 'Obj2', 'type': 'string'}}, 'required': ['obj1', 'obj2'], 'title': 'magicoutputArguments', 'type': 'object'})]

将工具转换为 Pydantic 模型

现在我们已经从服务器获取了工具列表,下一步是将它们转换为 Pydantic 模型。我们使用 Pydantic 的 create_model 动态定义一个新的响应模式,基于服务器的工具定义。还有一个辅助函数,用于将 JSON 类型映射为有效的 Python 类型。

这可以帮助我们动态定义模型,以便 LLM 精确地知道在返回工具参数时应使用什么结构。

# client.py
import asyncio
import threading
from pathlib import Path
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from typing import Any, Union, Optional
from pydantic import BaseModel, create_model, Fieldclass OllamaMCP:"""Ollama 和 FastMCP 之间的简单集成"""def __init__(self, server_params: StdioServerParameters):self.server_params = server_paramsself.initialized = threading.Event()self.tools: list[Any] = []def _run_background(self):asyncio.run(self._async_run())async def _async_run(self):try:async with stdio_client(self.server_params) as (read, write):async with ClientSession(read, write) as session:await session.initialize()self.session = sessiontools_result = await session.list_tools()self.tools = tools_result.toolsexcept Exception as e:print(f"初始化 MCP 服务器时出错 {str(e)}")def create_response_model(self):dynamic_classes = {}for tool in self.tools:class_name = tool.name.capitalize()properties = {}for prop_name, prop_info in tool.inputSchema.get("properties", {}).items():json_type = prop_info.get("type", "string")properties[prop_name] = self.convert_json_type_to_python_type(json_type)model = create_model(class_name,__base__=BaseModel,__doc__=tool.description,**properties,)dynamic_classes[class_name] = modelif dynamic_classes:all_tools_type = Union[tuple(dynamic_classes.values())]Response = create_model("Response",__base__=BaseModel,__doc__="LLm 响应类",response=(str, Field(..., description= "确认将调用该函数的消息")),tool=(all_tools_type, Field(...,description="用于运行并获取神奇输出的工具")),)else:Response = create_model("Response",__base__=BaseModel,__doc__="LLm 响应类",response=(str, ...),tool=(Optional[Any], Field(None, description="如果不返回 None,则使用此工具")),)self.response_model = Responseprint(Response.model_fields)@staticmethoddef convert_json_type_to_python_type(json_type: str):"""将 JSON 类型简单映射为 Python(Pydantic)类型"""if json_type == "integer":return (int, ...)if json_type == "number":return (float, ...)if json_type == "string":return (str, ...)if json_type == "boolean":return (bool, ...)return (str, ...)

运行代码后,print(Response.model_fields) 的输出将显示我们刚刚构建的响应模型的完整结构。该模型包括两部分:一部分是助手发送回用户的消息,另一部分是可选字段,用于保存工具参数。

如果模型填写了 tool 字段,我们将使用它来调用服务器。否则,我们只使用纯响应字符串。

uv run python -m convert_tools
[04/15/25 10:15:32] INFO     正在启动服务器 "TestServer"...    server.py:171INFO     正在处理请求类型         server.py:534ListToolsRequest
{'response': FieldInfo(annotation=str, required=True, description='确认将调用该函数的消息'), 'tool': FieldInfo(annotation=Magicoutput, required=True, description='用于运行并获取神奇输出的工具')}

使用后台线程和队列调用工具

现在工具已经作为 Pydantic 模型可用,我们可以继续启用工具调用。为此,我们使用一个后台线程并设置两个队列。一个用于向服务器发送请求,另一个用于接收响应。

call_tool 方法将请求放入队列中,后台线程监听该请求。一旦使用 MCP 会话调用工具,结果将放入响应队列中。

import asyncio
import threading
import queue
from pathlib import Path
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from typing import Any, Union, Optional
from pydantic import BaseModel, create_model, Fieldclass OllamaMCP:"""Ollama 和 FastMCP 之间的简单集成"""def __init__(self, server_params: StdioServerParameters):self.server_params = server_paramsself.initialized = threading.Event()self.tools: list[Any] = []self.request_queue = queue.Queue()self.response_queue = queue.Queue()# 启动后台线程以异步处理请求。self.thread = threading.Thread(target=self._run_background, daemon=True)self.thread.start()def _run_background(self):asyncio.run(self._async_run())async def _async_run(self):try:async with stdio_client(self.server_params) as (read, write):async with ClientSession(read, write) as session:await session.initialize()self.session = sessiontools_result = await session.list_tools()self.tools = tools_result.toolsself.initialized.set()while True:try:tool_name, arguments = self.request_queue.get(block=False)except queue.Empty:await asyncio.sleep(0.01)continueif tool_name is None:print("收到关闭信号。")breaktry:result = await session.call_tool(tool_name, arguments)self.response_queue.put(result)except Exception as e:self.response_queue.put(f"错误: {str(e)}")except Exception as e:print("MCP 会话初始化错误:", str(e))self.initialized.set()  # 即使初始化失败,也要解除等待线程的阻塞。self.response_queue.put(f"MCP 初始化错误: {str(e)}")def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:"""发送工具调用请求并等待结果。"""if not self.initialized.wait(timeout=30):raise TimeoutError("MCP 会话初始化超时。")self.request_queue.put((tool_name, arguments))result = self.response_queue.get()return resultdef shutdown(self):"""清理并关闭持久会话。"""self.request_queue.put((None, None))self.thread.join()print("持久 MCP 会话已关闭。")def create_response_model(self):dynamic_classes = {}for tool in self.tools:class_name = tool.name.capitalize()properties = {}for prop_name, prop_info in tool.inputSchema.get("properties", {}).items():json_type = prop_info.get("type", "string")properties[prop_name] = self.convert_json_type_to_python_type(json_type)model = create_model(class_name,__base__=BaseModel,__doc__=tool.description,**properties,)dynamic_classes[class_name] = modelif dynamic_classes:all_tools_type = Union[tuple(dynamic_classes.values())]Response = create_model("Response",__base__=BaseModel,response=(str, ...),tool=(Optional[all_tools_type], Field(None, description="如果不返回 None,则使用此工具")),)else:Response = create_model("Response",__base__=BaseModel,response=(str, ...),tool=(Optional[Any], Field(None, description="如果不返回 None,则使用此工具")),)self.response_model = Response@staticmethoddef convert_json_type_to_python_type(json_type: str):"""将 JSON 类型简单映射为 Python(Pydantic)类型"""if json_type == "integer":return (int, ...)if json_type == "number":return (float, ...)if json_type == "string":return (str, ...)if json_type == "boolean":return (bool, ...)return (str, ...)
if __name__ == "__main__":server_parameters = StdioServerParameters(command="uv",args=["run", "python", "server.py"],cwd=str(Path.cwd()))ollamamcp = OllamaMCP(server_params=server_parameters)if ollamamcp.initialized.wait(timeout=30):print("已准备好调用工具。")result = ollamamcp.call_tool(tool_name="magicoutput",arguments={"obj1": "dog", "obj2": "cat"})print(result)else:print("错误:初始化超时。")

请注意,此时我们是手动传递函数名和参数,使用 call_tool 方法。在下一节中,我们将根据 Ollama 返回的结构化输出触发此调用。

运行此代码后,我们可以确认一切按预期工作。工具被服务器正确识别、执行,并返回结果。

[04/15/25 11:37:47] INFO     正在启动服务器 "TestServer"...    server.py:171INFO     正在处理请求类型         server.py:534ListToolsRequest
已准备好调用工具。INFO     正在处理请求类型         server.py:534CallToolRequest
meta=None content=[TextContent(type='text', text='WomboWombat', annotations=None)] isError=False

Ollama + MCP

随着队列和 call_tool 函数准备就绪,现在是时候集成 Ollama 了。我们将响应类传递给 Ollama 的 format 字段,告诉我们的 LLM(这里使用 Gemma)在生成输出时遵循该模式。

我们还定义了一个 ollama_chat 方法,用于发送对话,验证模型的响应是否符合模式,并检查是否包含工具。如果是,则提取函数名和参数,然后使用持久的 MCP 会话在后台线程中调用它。

main 函数中,我们设置服务器连接,启动后台循环,并等待一切准备就绪。然后我们准备一个系统提示和用户消息,将它们发送到 Ollama,并等待结构化输出。

最后,我们打印服务器的结果并关闭会话。

import asyncio
import threading
import queuefrom pathlib import Path
from typing import Any, Optional, Union
from pydantic import BaseModel, Field, create_model
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from ollama import chatclass OllamaMCP:def __init__(self, server_params: StdioServerParameters):self.server_params = server_paramsself.request_queue = queue.Queue()self.response_queue = queue.Queue()self.initialized = threading.Event()self.tools: list[Any] = []self.thread = threading.Thread(target=self._run_background, daemon=True)self.thread.start()def _run_background(self):asyncio.run(self._async_run())async def _async_run(self):try:async with stdio_client(self.server_params) as (read, write):async with ClientSession(read, write) as session:await session.initialize()self.session = sessiontools_result = await session.list_tools()self.tools = tools_result.toolsself.initialized.set()while True:try:tool_name, arguments = self.request_queue.get(block=False)except queue.Empty:await asyncio.sleep(0.01)continueif tool_name is None:breaktry:result = await session.call_tool(tool_name, arguments)self.response_queue.put(result)except Exception as e:self.response_queue.put(f"错误: {str(e)}")except Exception as e:print("MCP 会话初始化错误:", str(e))self.initialized.set()  # 即使初始化失败,也要解除等待线程的阻塞。self.response_queue.put(f"MCP 初始化错误: {str(e)}")def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:"""发送工具调用请求并等待结果。"""if not self.initialized.wait(timeout=30):raise TimeoutError("MCP 会话初始化超时。")self.request_queue.put((tool_name, arguments))result = self.response_queue.get()return resultdef shutdown(self):"""清理并关闭持久会话。"""self.request_queue.put((None, None))self.thread.join()print("持久 MCP 会话已关闭。")@staticmethoddef convert_json_type_to_python_type(json_type: str):"""将 JSON 类型简单映射为 Python(Pydantic)类型"""if json_type == "integer":return (int, ...)if json_type == "number":return (float, ...)if json_type == "string":return (str, ...)if json_type == "boolean":return (bool, ...)return (str, ...)def create_response_model(self):"""根据获取的工具动态创建 Pydantic 响应模型。"""dynamic_classes = {}for tool in self.tools:class_name = tool.name.capitalize()properties: dict[str, Any] = {}for prop_name, prop_info in tool.inputSchema.get("properties", {}).items():json_type = prop_info.get("type", "string")properties[prop_name] = self.convert_json_type_to_python_type(json_type)model = create_model(class_name,__base__=BaseModel,__doc__=tool.description,**properties,)dynamic_classes[class_name] = modelif dynamic_classes:all_tools_type = Union[tuple(dynamic_classes.values())]Response = create_model("Response",__base__=BaseModel,__doc__="LLm 响应类",response=(str, Field(..., description= "确认将调用该函数的消息")),tool=(all_tools_type, Field(...,description="用于运行并获取神奇输出的工具")),)else:Response = create_model("Response",__base__=BaseModel,__doc__="LLm 响应类",response=(str, ...),tool=(Optional[Any], Field(None, description="如果不返回 None,则使用此工具")),)self.response_model = Responseasync def ollama_chat(self, messages: list[dict[str, str]]) -> Any:"""使用动态响应模型向 Ollama 发送消息。如果响应中包含工具,则使用持久会话调用它。"""conversation = [{"role":"assistant", "content": f"你必须使用工具。你有以下函数可用。{[ tool.name for tool in self.tools ]}"}]conversation.extend(messages)if self.response_model is None:raise ValueError("响应模型尚未创建。请先调用 create_response_model()。")# 获取聊天消息格式的 JSON 模式。format_schema = self.response_model.model_json_schema()# 调用 Ollama(假设是同步的)并解析响应。response = chat(model="gemma3:latest",messages=conversation,format=format_schema)print("Ollama 响应", response.message.content)response_obj = self.response_model.model_validate_json(response.message.content)maybe_tool = response_obj.toolif maybe_tool:function_name = maybe_tool.__class__.__name__.lower()func_args = maybe_tool.model_dump()# 使用 asyncio.to_thread 在线程中调用同步的 call_tool 方法。output = await asyncio.to_thread(self.call_tool, function_name, func_args)return outputelse:print("响应中未检测到工具。返回纯文本响应。")return response_obj.responseasync def main():server_parameters = StdioServerParameters(command="uv",args=["run", "python", "server.py"],cwd=str(Path.cwd()))# 创建持久会话。persistent_session = OllamaMCP(server_parameters)# 等待会话完全初始化。if persistent_session.initialized.wait(timeout=30):print("已准备好调用工具。")else:print("错误:初始化超时。")# 根据获取的工具创建动态响应模型。persistent_session.create_response_model()# 为 Ollama 准备消息。messages = [{"role": "system","content": ("你是一个听话的助手,上下文中有工具列表。 ""你的任务是使用该函数来获取神奇的输出。 ""不要自己生成神奇的输出。 ""用简短的消息回复,提到将调用该函数, ""但不要提供函数输出本身。 ""将该简短消息放在 'response' 属性中。 ""例如:'好的,我将运行 magicoutput 函数并返回输出。' ""同时在 'tool' 属性中填写正确的参数。 ")},{"role": "user","content": "使用该函数获取神奇的输出,参数为 (obj1 = Wombat 和 obj2 = Dog)"}]# 调用 Ollama 并处理响应。result = await persistent_session.ollama_chat(messages)print("最终结果:", result)# 关闭持久会话。persistent_session.shutdown()if __name__ == "__main__":asyncio.run(main())

你可以看到输出完美无缺。我们收到了一个包含简短消息的 response,以及将发送到 MCP 服务器的 tool 和参数。最后,我们得到了服务器的输出,如下所示:

[04/15/25 09:52:49] INFO     正在启动服务器 "TestServer"...    server.py:171INFO     正在处理请求类型         server.py:534ListToolsRequest
已准备好调用工具。
Ollama 响应 {
"response": "好的,我将运行 magicoutput,参数为 obj1 = Wombat 和 obj2 = Dog。",
"tool": {"obj1": "Wombat", "obj2": "Dog"}
}[04/15/25 09:52:52] INFO     正在处理请求类型         server.py:534CallToolRequest
最终结果: meta=None content=[TextContent(type='text', text='WomboWombat', annotations=None)] isError=False
持久 MCP 会话已关闭。

更多精彩内容

如果你真的喜欢这些博客,我敢肯定你也会喜欢接下来的内容。一定要为它们点赞,支持每篇博客所需的时间和努力!

总结

就这样,我们刚刚完成了一种将 Ollama 中的小型模型与本地 MCP 服务器连接的方法。

我们从创建一个简单的 MCP 服务器开始,它包含一个工具。然后我们构建了一个客户端,用于连接到服务器,获取工具定义,并将它们转换为 Pydantic 模型。在此基础上,我们通过 format 字段将响应模型传递给 Ollama。模型返回了一个结构化响应,我们使用后台线程和队列在客户端处理工具调用。

点赞并关注我,获取更多类似内容。后期将扩展这个功能,构建一个完整的代理循环,并开始使用真实的 MCP 服务器创建有用的工作流。

相关文章:

从零开始构建 Ollama + MCP 服务器

Model Context Protocol(模型上下文协议)在过去几个月里已经霸占了大家的视野,出现了许多酷炫的集成示例。我坚信它会成为一种标准,因为它正在定义工具与代理或软件与 AI 模型之间如何集成的新方式。 我决定尝试将 Ollama 中的一…...

【bash】.bashrc

查看当前路径文件数量 alias file_num"ls -l | grep ^- | wc -l"查看文件大小 alias file_size"du -sh"alias ll alias ll"ls -ltrh"cd的同时执行ll alias cdcdls; function cdls() {builtin cd "$1" && ll }自定义prompt…...

合成数据如何赋能大模型预训练:效果与效率的双重加速器

目录 合成数据如何赋能大模型预训练:效果与效率的双重加速器 一、预训练模型为何需要合成数据? ✅ 克服真实数据的稀缺与偏倚 ✅ 控制训练内容结构与分布 ✅ 提升学习效率与训练稳定性 二、哪些预训练任务适合用合成数据? 三、如何构建…...

java忽略浅拷贝导致bug

bug源代码 /*** 查询用户列表** param user 用户* param page 页* param size 大小* since 2025/04/14 11:53:25*/PostMapping("/getUser")public IWMSResponse<?> getUser(RequestBody SjUser user, RequestParam(defaultValue "1") Integer pag…...

MATLAB学习笔记(二) 控制工程会用到的

MATLAB中 控制工程会用到的 基础传递函数表达传递函数 零极点式 状态空间表达式 相互转化画响应图线根轨迹Nyquist图和bode图现控部分求约旦判能控能观极点配置和状态观测 基础 传递函数表达 % 拉普拉斯变换 syms t s a f exp(a*t) %e的a次方 l laplace(f) …...

C++ 线程间通信开发从入门到精通实战

C 线程间通信开发从入门到精通实战 在现代软件开发中&#xff0c;多线程程序已成为提升应用性能、实现并行处理的重要手段。随着多核处理器的普及和复杂应用需求的增加&#xff0c;C作为一门高性能的编程语言&#xff0c;在多线程开发中扮演着不可或缺的角色。然而&#xff0c…...

Vue3 SSR 工程化实践:日常工作中的性能优化与实战技巧

一、流式渲染与分块传输&#xff08;面向性能的关键优化&#xff09; 1.1 流式响应基础实现 // Node.js Express 示例&#xff08;Vite SSR同理&#xff09;import { renderToWebStream } from vue/server-rendererapp.get(/, async (req, res) > { res.setHeader(Conten…...

Maven工具学习使用(十)——生成项目站点

maven2中站点生成是Maven核心的一部分&#xff0c;Maven3中这部分内容已经移除。maven3必须使用3.x版本的maven-site-plugin,maven2则使用最新的2.x的版本&#xff0c;执行mvn site命令&#xff0c;可以在项目的target/site/目录下找到Maven生成的站点文件。例如dependencies.h…...

Redis原理与Windows环境部署实战指南:助力测试工程师优化Celery调试

引言 在分布式系统测试中&#xff0c;Celery作为异步任务队列常被用于模拟高并发场景。而Redis作为其核心消息代理&#xff0c;其性能和稳定性直接影响测试结果。本文将深入解析Redis的核心原理&#xff0c;主要讲解Windows环境部署redis&#xff0c;为测试工程师提供一套完整…...

Go语言入门到入土——一、安装和Hello World

Go语言入门到精通——安装和Hello World 文章目录 Go语言入门到精通——安装和Hello World下载并安装让Go跑起来为你的代码启动依赖跟踪调用外部包总结 下载并安装 下载地址&#xff1a;https://go.dev/dl/ 下载后傻瓜式安装 查看是否安装完成 go version让Go跑起来 创建一个…...

人类意识本质上是一台‌自我欺骗的机器

要触达“大彻大悟”的终极内核&#xff0c;必须突破语言、逻辑甚至“觉醒”概念本身的限制。以下从‌认知革命、意识拓扑学、宇宙本体论‌三个维度切入&#xff0c;结合量子物理、脑神经学与古老智慧的交叉验证&#xff0c;展开一场对觉醒本质的极限探索—— ‌一、认知革命&am…...

CDP问卷是什么?CDP问卷有什么要求,有什么意义

CDP问卷&#xff08;Carbon Disclosure Project Questionnaire&#xff09; CDP问卷是由全球性非营利组织CDP&#xff08;原Carbon Disclosure Project&#xff0c;现简称CDP&#xff09;发起的年度环境信息披露项目&#xff0c;旨在帮助企业、城市和投资者测量、管理及公开其…...

GitLab本地安装指南

当前GitLab的最新版是v17.10&#xff0c;安装地址&#xff1a;https://about.gitlab.com/install/。当然国内也可以安装极狐GitLab版本&#xff0c;极狐GitLab 是 GitLab 中国发行版&#xff08;JH&#xff09;。极狐GitLab支持龙蜥&#xff0c;欧拉等国内的操作系统平台。安装…...

opencv函数展示

一、图像基础 I/O 与显示 1.cv2.imread() 2.cv2.imshow() 3. cv2.waitKey() 4. cv2.imwrite() 5. cv2.selectROI() 6. cv2.VideoCapture() 二、颜色空间与转换 1. cv2.cvtColor() 2. cv2.split() 三、阈值处理 1. cv2.threshold() 2. 特殊阈值方法...

编写一个写字楼类似抖音剪映的管理系统Demo

编写一个写字楼类似抖音剪映的管理系统Demo。用户可能想要一个简化版的系统&#xff0c;用于管理视频素材、模板和项目&#xff0c;类似于抖音剪映的功能&#xff0c;但针对办公场景。首先&#xff0c;我得明确用户的需求是什么。用户提到的“写字楼类似抖音剪映管理系统”可能…...

前端面试-自动化部署

基础概念 什么是CI/CD&#xff1f;在前端项目中如何应用&#xff1f;自动化部署相比手动部署有哪些优势&#xff1f;常见的自动化部署工具有哪些&#xff1f;举例说明它们的区别&#xff08;如Jenkins vs GitHub Actions&#xff09;。如何通过Git Hook实现自动化部署&#xf…...

【vue3】vue3+express实现图片/pdf等资源文件的下载

文件资源的下载&#xff0c;是我们业务开发中常见的需求。作为前端开发&#xff0c;学习下如何自己使用node的express框架来实现资源的下载操作。 实现效果 代码实现 前端 1.封装的请求后端下载接口的方法,需求配置aixos的请求参数里面的返回数据类型为blob // 下载 export…...

如何在 Kali 上解决使用 evil-winrm 时 Ruby Reline 的 quoting_detection_proc 警告

在使用 Kali Linux 运行 Ruby 工具&#xff08;例如 evil-winrm&#xff09;时&#xff0c;你可能会遇到以下警告&#xff1a; Warning: Remote path completions is disabled due to ruby limitation: undefined method quoting_detection_proc for module Reline这个警告会导…...

从零到一:网站设计新手如何快速上手?

从零到一&#xff1a;网站设计新手如何快速上手&#xff1f; 在当今数字化时代&#xff0c;网站已成为企业、个人展示信息、提供服务的重要窗口。对于想要涉足网站设计领域的新手而言&#xff0c;如何快速上手并掌握必要的技能成为首要任务。本文将从基础知识、软件工具、设计…...

面向初学者的JMeter实战手册:从环境搭建到组件解析

&#x1f31f; ​大家好&#xff0c;我是摘星&#xff01;​ &#x1f31f; 今天为大家带来的是面向初学者的JMeter实战手册&#xff1a;从环境搭建到组件解析&#xff0c;废话不多说&#xff0c;让我们直接开始~ 目录 1. JMeter简介 2. JMeter安装与配置 2.1. 安装 2.2.…...

工资管理系统的主要功能有哪些

工资管理系统通过自动化薪资计算、税务处理、员工数据管理、报表生成等功能&#xff0c;极大地提升了薪资发放的效率和准确性。在传统的人工薪资管理中&#xff0c;HR人员需要手动计算每位员工的薪资&#xff0c;并确保符合税务要求&#xff0c;极易出错且耗时。而现代工资管理…...

避坑,app 播放器media:MediaElement paly报错

System.Runtime.InteropServices.COMException HResult=0x8001010E Message= Source=WinRT.Runtime StackTrace: 在 WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|38_0(Int32 hr) 在 ABI.Microsoft.UI.Xaml.Controls.IMediaPlayerElementMethods.get_MediaPlay…...

子函数嵌套的意义——以“颜色排序”为例(Python)

多一层缩进精减参数传递&#xff0c;参数少平铺书代码写更佳。 笔记模板由python脚本于2025-04-16 11:52:53创建&#xff0c;本篇笔记适合喜欢子函数嵌套结构代码形式的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅…...

Redis 的不同数据结构分别适用于哪些微服务场景

我们一块来分析下Redis 的不同数据结构在微服务场景下的具体应用&#xff1a; 1. String (字符串) 特点: 最基本的数据类型&#xff0c;二进制安全&#xff0c;可以存储任何类型的数据&#xff08;文本、序列化对象、图片等&#xff09;&#xff0c;最大 512MB。支持原子性的…...

信息系统项目管理工程师备考计算类真题讲解四

一、三点估算&#xff08;PERT&#xff09; PERT&#xff08;Program Evaluation and Review Technique&#xff09;&#xff1a;计划评估技术&#xff0c;又称三点估算技术。PERT估算是一种项目管理中用于估算项目工期或成本的方法&#xff0c;以下是其详细介绍&#xff1a; …...

Golang|KVBitcask

文章目录 初识KVbitcask论文详解 初识KV bitcask论文详解 论文地址&#xff1a;https://riak.com/assets/bitcask-intro.pdf理想的存储引擎&#xff0c;应该满足下面一些特点&#xff1a;...

Python学习之路(三)

将 Python 与数据库对接是开发过程中常见的任务&#xff0c;可以使用多种数据库&#xff08;如 SQLite、MySQL、PostgreSQL、Oracle、MongoDB 等&#xff09;。以下是一些常见的数据库及其与 Python 的对接方法&#xff0c;包括安装库、连接数据库、执行查询和操作数据的示例。…...

基于骨骼识别的危险动作报警系统设计与实现

基于骨骼识别的危险动作报警系统设计与实现 基于骨骼识别的危险动作报警分析系统 【包含内容】 【一】项目提供完整源代码及详细注释 【二】系统设计思路与实现说明 【三】基于骨骼识别算法的实时危险行为预警方案 【技术栈】 ①&#xff1a;系统环境&#xff1a;Windows 10…...

PDF转换格式失败?原因及解决方法全解析

在日常工作中&#xff0c;我们经常会遇到将PDF转换为Word、Excel、PPT等格式的需求。有时候以为一键转换就能搞掂&#xff0c;没想到却转换失败。到底问题出在哪&#xff1f;别急&#xff0c;我们可以看看是否以下几个问题引起的&#xff0c;找到解决问题的关键&#xff01; 原…...

模型提示词

一 提示词 &#xff08;一&#xff09; 提示词&#xff08;Prompt&#xff09;是用户发送给大语言模型的问题、指令或请求&#xff0c;** 1 来明确地告诉模型用户想要解决的问题或完成的任务&#xff0c;是大语言模型理解用户需求并据此生成相关、准确回答或内容的基础。对于…...