ollama for win + qwen2.5:14b + mcphost for go によるローカルllmでのmcp - end0tknr's kipple - web写経開発
先日の上記entryにある mcphost for go では上手く動作しませんでしたので、 今回は langchain-mcp-adapters for python を試します。
ちょうどよいタイミングで以下の参考urlを見つけ、全くの写経です
参考url
- Ollamaで動くMCPクライアントを作成する #LangChain - Qiita
- Ollamaを使用したMCPサーバーの構築と活用:詳細ガイド #AI - Qiita
- PyMCPAutoGUIでMCPを使ってみよう! #Python - Qiita
step1 langchain-mcp-adapters for python 等のinstall
CONDA> pip install langchain langchain-mcp-adapters langchain-ollama langgraph
step2 edit python mcp client
import asyncio import os import sys import json from langchain_mcp_adapters.client import MultiServerMCPClient from langgraph.prebuilt import create_react_agent from langchain_core.messages import SystemMessage from langchain_ollama import ChatOllama import sys llm = ChatOllama(model="qwen2.5:14b", temperature=0, base_url="http://localhost:11434" ) conf_path = "c:\\Users\\end0t\\.mcp.json" with open(conf_path, "r", encoding="utf-8") as f: conf = json.load(f) mcp_conf = conf.get("mcpServers", {}) sys_prompt = """\ あなたはMCPサーバーを使用するAIアシスタントです。 MCPサーバーのToolの結果を優先し回答してください。 回答は日本語でお願いします。 """ user_prompt = """\ 日本の首都はどこですか? 回答は file-system ツールを利用し、 c:/Users/end0t/tmp/capitals.txt のテキストファイルへ記入して下さい """ def print_messages_by_type(result): messages = result.get("messages", result) if not isinstance(messages, list): print("messagesがリストではありません") return for msg in messages: # クラス名またはtype属性で判定 msg_type = msg.get("type") if isinstance(msg, dict) else type(msg).__name__ if not msg_type and hasattr(msg, "__class__"): msg_type = msg.__class__.__name__ print(f"\n--- {msg_type} ---") if isinstance(msg, dict): for k, v in msg.items(): print(f"{k}: {v}") else: print(msg) async def main(): async with MultiServerMCPClient( mcp_conf ) as client: sys_message = SystemMessage( content=sys_prompt ) agent = create_react_agent(llm, client.get_tools(), prompt=sys_message ) result = await agent.ainvoke({ "messages":user_prompt}) for message in result["messages"]: print( f"{message}" ) if __name__ == "__main__": asyncio.run(main())
step3 test
「ollama serve」コマンド等で、ollama を起動した上で、以下を実行。
回答は、なぜかファイルではなく、sqlite へ記入されましたが、 前回よりは前進した気がしますので、今回は、これでokとします。
Exception ignored や Traceback が表示されましたが、原因等については、 後日、気が向いたら調べます
content='日本の首都はどこですか?\n回答は file-system ツールを利用し、\nc:/Users/end0t/tmp/capitals.txt のテキストファイルへ記入して下さい\n' additional_kwargs={} response_metadata={} id='69eafda2-5579-4cd4-936b-e489dbcc8e0e'
content='' additional_kwargs={} response_metadata={'model': 'qwen2.5:14b', 'created_at': '2025-04-28T04:46:11.8772824Z', 'done': True, 'done_reason': 'stop', 'total_duration': 3200511600, 'load_duration': 81585400, 'prompt_eval_count': 2048, 'prompt_eval_duration': 698845800, 'eval_count': 161, 'eval_duration': 2367378000, 'model_name': 'qwen2.5:14b'} id='run-a4d5cde6-08ec-405a-a876-ce303673e91e-0' tool_calls=[{'name': 'list_allowed_directories', 'args': {}, 'id': 'b75609eb-d22d-43ad-a6fa-d6b6cd1f9ae9', 'type': 'tool_call'}] usage_metadata={'input_tokens': 2048, 'output_tokens': 161, 'total_tokens': 2209}
content='Allowed directories:\nC:\\Users\\end0t\\tmp' name='list_allowed_directories' id='89fcf9eb-4b95-46fb-a56f-373ddd595575' tool_call_id='b75609eb-d22d-43ad-a6fa-d6b6cd1f9ae9'
content='The server is configured to allow access to the following directory:\n\n- `C:\\Users\\end0t\\tmp`\n\nThis means you can perform file operations within this directory and its subdirectories. If you need to work with files in other directories, please let me know so I can assist further or escalate the request for additional permissions.' additional_kwargs={} response_metadata={'model': 'qwen2.5:14b', 'created_at': '2025-04-28T04:46:13.4472951Z', 'done': True, 'done_reason': 'stop', 'total_duration': 1563930700, 'load_duration': 42028900, 'prompt_eval_count': 2048, 'prompt_eval_duration': 460481100, 'eval_count': 68, 'eval_duration': 1026382500, 'model_name': 'qwen2.5:14b'} id='run-187f20ae-4c9a-44e6-a4b8-11d0173006e8-0' usage_metadata={'input_tokens': 2048, 'output_tokens': 68, 'total_tokens': 2116}
Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x000001D18F7A5440>
Traceback (most recent call last):
File "C:\Users\end0t\miniconda3\Lib\asyncio\base_subprocess.py", line 126, in __del__
self.close()
File "C:\Users\end0t\miniconda3\Lib\asyncio\base_subprocess.py", line 104, in close
proto.pipe.close()
File "C:\Users\end0t\miniconda3\Lib\asyncio\proactor_events.py", line 109, in close
self._loop.call_soon(self._call_connection_lost, None)
File "C:\Users\end0t\miniconda3\Lib\asyncio\base_events.py", line 794, in call_soon
self._check_closed()
File "C:\Users\end0t\miniconda3\Lib\asyncio\base_events.py", line 540, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x000001D18F7A6C00>
Traceback (most recent call last):
File "C:\Users\end0t\miniconda3\Lib\asyncio\proactor_events.py", line 116, in __del__
_warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
^^^^^^^^
File "C:\Users\end0t\miniconda3\Lib\asyncio\proactor_events.py", line 80, in __repr__
info.append(f'fd={self._sock.fileno()}')
^^^^^^^^^^^^^^^^^^^
File "C:\Users\end0t\miniconda3\Lib\asyncio\windows_utils.py", line 102, in fileno
raise ValueError("I/O operation on closed pipe")
ValueError: I/O operation on closed pipe
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x000001D18F7A6C00>
Traceback (most recent call last):
File "C:\Users\end0t\miniconda3\Lib\asyncio\proactor_events.py", line 116, in __del__
_warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
^^^^^^^^
File "C:\Users\end0t\miniconda3\Lib\asyncio\proactor_events.py", line 80, in __repr__
info.append(f'fd={self._sock.fileno()}')
^^^^^^^^^^^^^^^^^^^
File "C:\Users\end0t\miniconda3\Lib\asyncio\windows_utils.py", line 102, in fileno
raise ValueError("I/O operation on closed pipe")
ValueError: I/O operation on closed pipe
Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x000001D18F7A5440>
Traceback (most recent call last):
File "C:\Users\end0t\miniconda3\Lib\asyncio\base_subprocess.py", line 125, in __del__
_warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
^^^^^^^^
File "C:\Users\end0t\miniconda3\Lib\asyncio\base_subprocess.py", line 78, in __repr__
info.append(f'stdout={stdout.pipe}')
^^^^^^^^^^^^^
File "C:\Users\end0t\miniconda3\Lib\asyncio\proactor_events.py", line 80, in __repr__
info.append(f'fd={self._sock.fileno()}')
^^^^^^^^^^^^^^^^^^^
File "C:\Users\end0t\miniconda3\Lib\asyncio\windows_utils.py", line 102, in fileno
raise ValueError("I/O operation on closed pipe")
ValueError: I/O operation on closed pipe