使用 mcp-agent 构建 Streamlit 多 MCP 工具 Agent 聊天应用:以 Finder Agent 为例
【免费下载链接】mcp-agentBuild effective agents using Model Context Protocol and simple workflow patterns项目地址: https://gitcode.com/GitHub_Trending/mc/mcp-agent
导读
本文基于 mcp-agent 仓库中的streamlit_mcp_basic_agent示例(examples/usecases/streamlit_mcp_basic_agent/README.md),讲解如何用 Streamlit 快速搭建一个可视化聊天界面,并在其后接入一个同时拥有fetch(抓取 URL)与filesystem(读取本地文件)两个 MCP 服务器的 "Finder" Agent。读完本文,你将掌握 mcp-agent 环境搭建、MCPApp与Agent的初始化模式、Streamlitsession_state与异步 Agent 的配合技巧、会话历史回传,以及基于RequestParams的增强 LLM 调用方法,可直接复刻或改造成你自己的多工具聊天应用。
示例的架构如下:Streamlit 前端负责展示与交互,Finder Agent 是决策中枢,它会根据用户请求自行判断该调用哪个 MCP 服务器提供的工具:
┌───────────┐ ┌──────────┐ ┌──────────────┐ │ Streamlit │─────▶│ Finder │──┬──▶│ Fetch │ │ App │ │ Agent │ │ │ MCP Server │ └───────────┘ └──────────┘ │ └──────────────┘ │ ┌──────────────┐ └──▶│ Filesystem │ │ MCP Server │ └──────────────┘1. 示例整体概览:一个会"自己决定用哪个工具"的 Finder Agent
该示例展示了一个名为 "Finder" 的 Agent:它同时接入fetch与filesystem两个 MCP 服务器,因此既能获取网络 URL 的内容,也能读取本地文件系统。你无需事先指定用哪个工具——Agent 会根据你的提问(例如"本地某个文件里写了什么"或"这个网页讲了什么")自行判断何时调用哪个工具,最终返回与请求最匹配的结果(URI 与内容)。
从实现层面看,Finder Agent 的核心逻辑定义在 examples/usecases/streamlit_mcp_basic_agent/main.py 中:
state = await get_agent_state( key="finder_agent", agent_class=Agent, llm_class=OpenAIAugmentedLLM, name="finder", instruction="""You are an agent with access to the filesystem, as well as the ability to fetch URLs. Your job is to identify the closest match to a user's request, make the appropriate tool calls, and return the URI and CONTENTS of the closest match.""", server_names=["fetch", "filesystem"], )其中server_names=["fetch", "filesystem"]声明了 Agent 可访问的 MCP 服务器集合;instruction以自然语言的方式定义了 Agent 的职责与行为准则(识别最接近请求的目标、调用合适工具、返回 URI 和内容),这正是 mcp-agent 中"Agent 应通过 instruction 定义其用途"这一设计思想的体现——在 src/mcp_agent/agents/agent.py 中,Agent类的核心字段正是name、instruction与server_names。
2. 第一步:克隆仓库并定位到示例目录
先克隆 mcp-agent 仓库并进入示例目录:
git clone https://github.com/lastmile-ai/mcp-agent.git cd mcp-agent/examples/usecase/streamlit_mcp_basic_agent注:本仓库中该示例的完整文件位于 examples/usecases/streamlit_mcp_basic_agent/,包含
main.py、mcp_agent.config.yaml、requirements.txt与mcp_agent.secrets.yaml.example。
如果你还没有安装uv(Python 包管理器),先安装它:
pip install uv同步 mcp-agent 项目本身的依赖:
uv sync再安装本示例专属的额外依赖:
uv pip install -r requirements.txtrequirements.txt的内容表明:除了以本地路径方式(mcp-agent @ file://../../../,指向仓库根目录)引入 mcp-agent 框架外,本示例还额外依赖openai(驱动OpenAIAugmentedLLM)与streamlit(前端界面):
# Core framework dependency mcp-agent @ file://../../../ # Link to the local mcp-agent project root # Additional dependencies specific to this example openai streamlit3. 第二步:配置密钥与环境变量
复制密钥模板并填写真实配置:
cp mcp_agent.secrets.yaml.example mcp_agent.secrets.yaml然后打开mcp_agent.secrets.yaml,为你选择的 LLM 提供商填入 API Key。模板文件 examples/usecases/streamlit_mcp_basic_agent/mcp_agent.secrets.yaml.example 的默认结构同时预留了 OpenAI 与 Anthropic 两家的密钥位置:
$schema: ../../../schema/mcp-agent.config.schema.json openai: api_key: openai_api_key anthropic: api_key: anthropic_api_key安全实践:
mcp_agent.secrets.yaml存放真实密钥,应当加入.gitignore,切勿提交到版本库。示例中mcp_agent.config.yaml内的注释也明确提示:"Secrets (API keys, etc.) are stored in an mcp_agent.secrets.yaml file which can be gitignored"。
4. 第三步:在本地运行 Streamlit 应用
一切就绪后,用uv启动:
uv run streamlit run main.py启动后浏览器会自动打开 Streamlit 界面(默认 http://localhost:8501)。在输入框中输入任意与本地文件或 URL 相关的问题,Finder Agent 就会开始工作。
5. 深入理解:应用是如何组装起来的
main.py是理解整个示例的关键。它的组装链路可以拆成四层:
5.1 创建应用入口:MCPApp
if __name__ == "__main__": app = MCPApp(name="mcp_basic_agent") asyncio.run(main())MCPApp是 mcp-agent 的主应用类,负责管理全局状态并托管工作流。从 src/mcp_agent/app.py 的实现看,它在构造时会自动加载配置:当不显式传入settings时,会通过get_settings()从mcp_agent.config.yaml读取配置;当传入的是字符串时,则被当作配置文件路径处理。MCPApp还负责注册 task/decorator/signal 等注册表、初始化日志与追踪系统。在示例的main()中,第一行就是:
await app.initialize()initialize()会完成环境绑定(如加载.env文件)、创建全局Context、初始化 MCP 服务器连接所需的执行器与服务器注册表等基础设施。
5.2 初始化 Agent 并挂载 LLM:get_agent_state
示例定义了一个AgentState数据类作为 Agent 及其关联 LLM 的容器:
@dataclass class AgentState: """Container for agent and its associated LLM""" agent: Agent llm: Optional[OpenAIAugmentedLLM] = None随后get_agent_state是面向 Streamlit 的关键封装——它负责"获取或创建 Agent 状态,若从会话中取回则重新初始化连接":
async def get_agent_state( key: str, agent_class: Type[Agent], llm_class: Optional[Type[T]] = None, **agent_kwargs, ) -> AgentState: if key not in st.session_state: # Create new agent agent = agent_class( connection_persistence=False, **agent_kwargs, ) await agent.initialize() # Attach LLM if specified llm = None if llm_class: llm = await agent.attach_llm(llm_class) state: AgentState = AgentState(agent=agent, llm=llm) st.session_state[key] = state else: state = st.session_state[key] return state这里有两个重要的技术细节:
connection_persistence=False:在 Streamlit 这类"每次交互可能重新运行脚本"的场景中,MCP 服务器连接不持久化,避免会话复用导致连接状态失效。Agent类的该字段在 src/mcp_agent/agents/agent.py 中默认值为True(持久化连接),本示例显式关闭以适配 Streamlit 的重运行模型。agent.initialize()与agent.attach_llm(llm_class):initialize()会通过执行器向server_names中声明的 MCP 服务器发起连接,并聚合各服务器的工具、提示词、资源元数据(从源码看,初始化结果会填充_namespaced_tool_map、_server_to_tool_map等内部映射,agent.list_tools()后续正是基于这些映射返回工具列表)。attach_llm(llm_factory)则为 Agent 创建AugmentedLLM实例(此处为OpenAIAugmentedLLM),并把 Agent 的instruction关联到 LLM 上。
由于st.session_state中缓存的是可序列化之外的对象引用,脚本重跑时从 session 取回的 Agent 需要重新建立连接,因此该封装把"创建"与"复用"两条路径都收敛到了一处。
5.3 展示 Agent 可见的工具清单:list_tools
tools = await state.agent.list_tools() tools_str = format_list_tools_result(tools)format_list_tools_result将ListToolsResult渲染为 Markdown 列表:
def format_list_tools_result(list_tools_result: ListToolsResult): res = "" for tool in list_tools_result.tools: res += f"- **{tool.name}**: {tool.description}\n\n" return res随后在界面中通过可折叠区域展示:
with st.expander("View Tools"): st.markdown(tools_str)用户展开 "View Tools" 即可看到 Finder Agent 当前实际可用的全部工具(来自fetch与filesystem两个服务器的工具会被聚合在一起)。
5.4 搭建聊天界面与消息循环
界面主体分为三部分:
标题区:
st.title("💬 Basic Agent Chatbot") st.caption("🚀 A Streamlit chatbot powered by mcp-agent")消息历史渲染:
if "messages" not in st.session_state: st.session_state["messages"] = [ {"role": "assistant", "content": "How can I help you?"} ] for msg in st.session_state["messages"]: st.chat_message(msg["role"]).write(msg["content"])输入与生成:
if prompt := st.chat_input("Type your message here..."): st.session_state["messages"].append({"role": "user", "content": prompt}) st.chat_message("user").write(prompt) with st.chat_message("assistant"): response = "" with st.spinner("Thinking..."): # Pass the conversation history to the LLM conversation_history = st.session_state["messages"][ 1: ] # Skip the initial greeting response = await state.llm.generate_str( message=prompt, request_params=RequestParams( use_history=True, history=conversation_history, # Pass the conversation history ), ) st.markdown(response) st.session_state["messages"].append({"role": "assistant", "content": response})这段代码的要点:
- 用户输入被追加进
st.session_state["messages"]后,脚本用conversation_history = st.session_state["messages"][1:]切片跳过系统欢迎语,把真实对话历史传给 LLM,实现多轮上下文理解。 state.llm.generate_str(...)是AugmentedLLMProtocol定义的增强生成接口(见 src/mcp_agent/workflows/llm/augmented_llm.py),它返回字符串形式的生成结果。与普通 LLM 调用不同,generate_str内部会运行最多max_iterations(默认 10 次)的多轮 agentic 循环——模型在循环中自主决定调用哪个 MCP 工具、读取返回结果、继续推理,直到得出最终答案。这正是 Finder Agent "自己判断何时用 fetch、何时用 filesystem"的能力来源。- 由于
main()是异步函数,入口处使用asyncio.run(main())驱动整个事件循环;Streamlit 的st.spinner保证了生成期间的界面反馈。
5.5RequestParams:控制生成行为
示例用RequestParams(use_history=True, history=conversation_history)显式开启历史注入。RequestParams在 src/mcp_agent/workflows/llm/augmented_llm.py 中定义,它继承自 MCP 的CreateMessageRequestParams并扩展了若干生成参数,常用字段包括:
| 字段 | 默认值 | 含义 |
|---|---|---|
use_history | True | 是否在生成请求中包含消息历史 |
history | 无 | 显式传入的历史消息列表(配合use_history=True使用) |
maxTokens | 2048 | 允许采样的最大 token 数 |
max_iterations | 10 | LLM 循环运行的最大迭代次数(工具调用链深度上限) |
model | None | 指定生成使用的模型,覆盖modelPreferences选择逻辑 |
parallel_tool_calls | False | 是否允许并行工具调用 |
reasoning_effort | None | 仅 OpenAI 系:控制 o1/o3/o4/gpt-5 系列模型的推理强度(none/low/medium/high) |
在 Finder 场景中,把use_history=True与传入的历史切片组合,即可保证多轮对话中模型始终"记得"之前问过什么,从而在连续追问时做出正确的工具选择。
6. 配置文件解析:MCP 服务器与模型声明
示例的 mcp_agent.config.yaml 声明了执行引擎、日志与两个 MCP 服务器:
$schema: ../../../schema/mcp-agent.config.schema.json execution_engine: asyncio logger: type: console level: debug batch_size: 100 flush_interval: 2 max_queue_size: 2048 http_endpoint: http_headers: http_timeout: 5 progress_display: false mcp: servers: fetch: command: "uvx" args: ["mcp-server-fetch"] filesystem: command: "npx" args: ["-y", "@modelcontextprotocol/server-filesystem", "."] openai: # Secrets (API keys, etc.) are stored in an mcp_agent.secrets.yaml file which can be gitignored default_model: gpt-4o各段含义:
execution_engine: asyncio:选择 asyncio 作为执行引擎(mcp-agent 也支持 Temporal 等引擎,详见 docs/concepts/execution-engines.mdx)。logger:控制台日志输出,level: debug便于开发期观察工具调用与事件流;batch_size、flush_interval、max_queue_size等字段控制异步日志队列行为。mcp.servers:以命令 + 参数方式声明两个 stdio 型 MCP 服务器。fetch通过uvx运行mcp-server-fetch提供 URL 抓取能力;filesystem通过npx运行官方@modelcontextprotocol/server-filesystem,其参数"."表示把当前目录作为可访问根目录。openai.default_model: gpt-4o:指定默认使用 GPT-4o 模型;OpenAIAugmentedLLM正是基于该配置驱动工具调用循环。真正的api_key放在mcp_agent.secrets.yaml中,配置文件与密钥文件分离。
依赖提示:
fetch服务器要求环境可运行uvx(安装uv后可用),filesystem服务器要求环境可运行npx(Node.js 生态,首次运行会自动下载@modelcontextprotocol/server-filesystem包)。若机器上缺少对应运行时,Agent 初始化阶段会因无法拉起子进程而失败。
7. 工作原理小结:一条从界面到 MCP 服务器的完整链路
结合源码,整个应用的一次对话请求会经历如下链路:
- Streamlit 捕获输入并更新
st.session_state["messages"]; main()从 session 中取出(或重建)Finder Agent 与OpenAIAugmentedLLM;llm.generate_str(message, RequestParams(use_history=True, history=...))启动 agentic 生成循环;- 模型在循环中根据
server_names暴露的工具集选择调用fetch或filesystem的工具,工具调用经由MCPAggregator与各服务器会话(stdin/stdout 传输)执行,结果回填给模型继续推理(src/mcp_agent/mcp/mcp_aggregator.py); - 循环收敛后返回最终字符串,Streamlit 以
st.markdown渲染,并追加到消息历史,供下一轮继续使用。
8. 扩展思路
基于该示例,可以低成本地演进出更多能力:
- 更换 MCP 服务器:在
mcp_agent.config.yaml中新增服务器条目并在server_names中声明,即可让 Finder Agent 拥有数据库查询、HTTP 请求、浏览器操作等新工具,无需改动界面代码。 - 更换模型提供商:
OpenAIAugmentedLLM替换为其他AugmentedLLM子类,或调整openai.default_model,即可切换后端模型。 - 接入流式输出:改用
generate_str_stream逐 token 渲染,提升长回复的实时体验。 - 接入结构化输出:使用
generate_structured让模型输出受 Pydantic 模型约束的结构化结果,便于后续程序化处理。
参考与延伸阅读
- 示例源码:examples/usecases/streamlit_mcp_basic_agent/main.py
- 示例配置:examples/usecases/streamlit_mcp_basic_agent/mcp_agent.config.yaml
- 示例密钥模板:examples/usecases/streamlit_mcp_basic_agent/mcp_agent.secrets.yaml.example
Agent类实现:src/mcp_agent/agents/agent.pyMCPApp应用入口:src/mcp_agent/app.pyRequestParams与AugmentedLLM定义:src/mcp_agent/workflows/llm/augmented_llm.pyOpenAIAugmentedLLM实现:src/mcp_agent/workflows/llm/augmented_llm_openai.py- 配置项与执行引擎说明:docs/configuration.mdx、docs/concepts/execution-engines.mdx
- 更多 MCP 接入示例:examples/mcp/
【免费下载链接】mcp-agentBuild effective agents using Model Context Protocol and simple workflow patterns项目地址: https://gitcode.com/GitHub_Trending/mc/mcp-agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考