Streamlit集成MCP Client遇NotImplemented错误求助
Streamlit集成MCP Client时的NotImplementedError问题解决
错误原因
从错误栈追踪可知,问题根源在于Windows系统下Streamlit默认使用的SelectorEventLoop不支持异步子进程操作。当MCP Client尝试通过anyio/asyncio创建子进程时,SelectorEventLoop的_make_subprocess_transport方法直接抛出NotImplementedError。nest_asyncio未生效的原因是Streamlit的事件循环初始化逻辑会覆盖手动设置的循环,导致ProactorEventLoop没被正确启用。
解决方案
方案1:强制启用ProactorEventLoop并修复nest_asyncio应用时机
在Streamlit脚本最顶部添加以下代码,确保事件循环在Streamlit初始化前就被替换:
import asyncio import nest_asyncio import sys if sys.platform == "win32": # Windows下强制使用支持子进程的ProactorEventLoop asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) # 应用nest_asyncio允许嵌套事件循环 nest_asyncio.apply() import streamlit as st # 后续代码...
方案2:将MCP Client异步操作移至独立线程
避免在Streamlit的事件循环中直接运行异步子进程操作,用线程隔离:
import threading import asyncio # 封装MCP连接逻辑为同步函数 def run_mcp_connection(script_path): mcp_client = MCPClient() asyncio.run(mcp_client.connect_to_server(script_path)) # Streamlit按钮逻辑修改 available_scripts = ["mcp_server1.py", "mcp_server2.py"] selected_script = st.selectbox("MCP server script", available_scripts) if st.button("Enable MCP server"): # 启动线程执行MCP连接 threading.Thread(target=run_mcp_connection, args=(selected_script,), daemon=True).start() st.success("MCP server connection started in background")
方案3:使用Streamlit的异步支持特性
Streamlit 1.28+支持异步函数作为回调,可直接使用st.button的on_click参数绑定异步函数(需确保事件环已正确配置):
async def connect_mcp(script_path): mcp_client = MCPClient() await mcp_client.connect_to_server(script_path) st.success("MCP server connected successfully") available_scripts = ["mcp_server1.py", "mcp_server2.py"] selected_script = st.selectbox("MCP server script", available_scripts) st.button("Enable MCP server", on_click=connect_mcp, args=(selected_script,))
相关代码片段
Streamlit原代码
available_scripts = ["mcp_server1.py", "mcp_server2.py"] selected_script = st.selectbox("MCP server script", available_scripts) if st.button("Enable MCP server"): mcp_client = MCPClient() asyncio.run(mcp_client.connect_to_server(selected_script))
MCPClient实现代码
class MCPClient: def __init__(self): self.session: ClientSession | None = None self.exit_stack = AsyncExitStack() async def connect_to_server(self, server_script_path: str): 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)) self.stdio, self.write = stdio_transport self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write)) await self.session.initialize()
完整错误栈
File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\streamlit\runtime\scriptrunner\exec_code.py", line 121, in exec_func_with_error_handling result = func() ^^^^^^ File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\streamlit\runtime\scriptrunner\script_runner.py", line 595, in code_to_exec self._session_state.on_script_will_rerun( File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\streamlit\runtime\state\safe_session_state.py", line 68, in on_script_will_rerun self._state.on_script_will_rerun(latest_widget_states) File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\streamlit\runtime\state\session_state.py", line 558, in on_script_will_rerun self._call_callbacks() File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\streamlit\runtime\state\session_state.py", line 571, in _call_callbacks self._new_widget_state.call_callback(wid) File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\streamlit\runtime\state\session_state.py", line 272, in call_callback callback(*args, **kwargs) File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\app.py", line 146, in toggle_mcp asyncio.run(mcp_client.test(st.session_state["mcp_script"])) File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\nest_asyncio.py", line 30, in run return loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\nest_asyncio.py", line 98, in run_until_complete return f.result() ^^^^^^^^^^ File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\asyncio\futures.py", line 203, in result raise self._exception.with_traceback(self._exception_tb) File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\asyncio\tasks.py", line 277, in __step result = coro.send(None) ^^^^^^^^^^^^^^^ File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\mcp_client.py", line 52, in test stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\contextlib.py", line 650, in enter_async_context result = await _enter(cm) ^^^^^^^^^^^^^^^^ File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\contextlib.py", line 210, in __aenter__ return await anext(self.gen) ^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\mcp\client\stdio\__init__.py", line 115, in stdio_client process = await _create_platform_compatible_process( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\mcp\client\stdio\__init__.py", line 230, in _create_platform_compatible_process process = await create_windows_process(command, args, env, errlog, cwd) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\mcp\client\stdio\win32.py", line 85, in create_windows_process process = await anyio.open_process( ^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\anyio\_core\_subprocesses.py", line 190, in open_process return await get_async_backend().open_process( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\danid\Documents\Personal\Projects\llm_chat_room\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 2561, in open_process process = await asyncio.create_subprocess_exec( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\asyncio\subprocess.py", line 223, in create_subprocess_exec transport, protocol = await loop.subprocess_exec( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\asyncio\base_events.py", line 1708, in subprocess_exec transport = await self._make_subprocess_transport( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\asyncio\base_events.py", line 503, in _make_subprocess_transport raise NotImplementedError
内容的提问来源于stack exchange,提问作者someguy
相关产品推荐
相关产品推荐

