使用 mcp-agent 构建 Streamlit 多 MCP 工具 Agent 聊天应用:以 Finder Agent 为例
2026/9/16 18:31:39 网站建设 项目流程

使用 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 环境搭建、MCPAppAgent的初始化模式、Streamlitsession_state与异步 Agent 的配合技巧、会话历史回传,以及基于RequestParams的增强 LLM 调用方法,可直接复刻或改造成你自己的多工具聊天应用。

示例的架构如下:Streamlit 前端负责展示与交互,Finder Agent 是决策中枢,它会根据用户请求自行判断该调用哪个 MCP 服务器提供的工具:

┌───────────┐ ┌──────────┐ ┌──────────────┐ │ Streamlit │─────▶│ Finder │──┬──▶│ Fetch │ │ App │ │ Agent │ │ │ MCP Server │ └───────────┘ └──────────┘ │ └──────────────┘ │ ┌──────────────┐ └──▶│ Filesystem │ │ MCP Server │ └──────────────┘

1. 示例整体概览:一个会"自己决定用哪个工具"的 Finder Agent

该示例展示了一个名为 "Finder" 的 Agent:它同时接入fetchfilesystem两个 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类的核心字段正是nameinstructionserver_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.pymcp_agent.config.yamlrequirements.txtmcp_agent.secrets.yaml.example

如果你还没有安装uv(Python 包管理器),先安装它:

pip install uv

同步 mcp-agent 项目本身的依赖:

uv sync

再安装本示例专属的额外依赖:

uv pip install -r requirements.txt

requirements.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 streamlit

3. 第二步:配置密钥与环境变量

复制密钥模板并填写真实配置:

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_resultListToolsResult渲染为 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 当前实际可用的全部工具(来自fetchfilesystem两个服务器的工具会被聚合在一起)。

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_historyTrue是否在生成请求中包含消息历史
history显式传入的历史消息列表(配合use_history=True使用)
maxTokens2048允许采样的最大 token 数
max_iterations10LLM 循环运行的最大迭代次数(工具调用链深度上限)
modelNone指定生成使用的模型,覆盖modelPreferences选择逻辑
parallel_tool_callsFalse是否允许并行工具调用
reasoning_effortNone仅 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_sizeflush_intervalmax_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 服务器的完整链路

结合源码,整个应用的一次对话请求会经历如下链路:

  1. Streamlit 捕获输入并更新st.session_state["messages"]
  2. main()从 session 中取出(或重建)Finder Agent 与OpenAIAugmentedLLM
  3. llm.generate_str(message, RequestParams(use_history=True, history=...))启动 agentic 生成循环;
  4. 模型在循环中根据server_names暴露的工具集选择调用fetchfilesystem的工具,工具调用经由MCPAggregator与各服务器会话(stdin/stdout 传输)执行,结果回填给模型继续推理(src/mcp_agent/mcp/mcp_aggregator.py);
  5. 循环收敛后返回最终字符串,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.py
  • MCPApp应用入口:src/mcp_agent/app.py
  • RequestParamsAugmentedLLM定义:src/mcp_agent/workflows/llm/augmented_llm.py
  • OpenAIAugmentedLLM实现: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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询