news 2026/9/10 5:28:08

WebMCP:让AI Agent通过标准接口操作网页的轻量方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WebMCP:让AI Agent通过标准接口操作网页的轻量方案

最近我有一个非常强烈的感受:AI Agent 要真正落地,功夫一半在大模型本身的推理能力上,另一半其实在“它能不能摸到你系统里的工具”。如果你跟我一样折腾过让大模型去操作网页、查页面状态、触发业务流程,那你大概率遇到过这个尴尬事——模型很聪明,但你的网页对它来说是个“暗盒”。它能读懂你粘贴给它的 HTML,却没办法随时、按需地调用网页里的真实功能。WebMCP 就是我这段时间整理出来的一套偏轻量的方法,核心目标很简单:让网页把自己能做的事情,通过一个标准接口暴露给 AI Agent。Agent 不需要知道按钮挂在哪个 DOM 节点、接口地址藏在哪个目录,它只管说“我要做什么”,网页负责“去怎么做”。

这套思路适合谁?适合正在手工搭 Agent 工具链的开发者、准备把网站能力接入大模型的产品同学,也包括那些想把内部系统、运营后台、个人知识库开放给 Agent 的爱好者。技术成熟度上,大模型和多模态交互已经具备了量产落地条件,Agent 从 demo 走向生产的关键,恰恰就是工具层的标准化和小步快跑的落地姿势。下面我把这套方案从设计思路到实操过程、再到踩坑记录,一次性整理出来。

1. 为什么 WebMCP 值得自己搭一套

1.1 大模型缺少的不是智商,是一双能“动手”的手

很多人在接触 Function Calling 之后都有一种豁然开朗的感觉:原来可以让模型输出一个 JSON,然后程序根据 JSON 去调用真实的函数。但真到了网页场景里,问题马上来了。网页不是 Python 函数,它有自己的生命周期:按钮有时可见有时不可见,表单可能要等接口返回才能提交,一个页面上有成百上千个元素,总不能每个都写一个函数让模型调用吧。

我见过不少项目的做法是给 Agent 塞一堆硬编码函数,比如check_order(),create_order(),query_user()。这种模式在接口固定、页面结构固定的系统里能跑,但一套稍复杂的运营后台,工具函数能堆出几十个,且每一个都要手动维护参数说明。更麻烦的是,页面升级、功能下架,文档和代码往往不同步,Agent 调用到一个已经失效的工具是常有的事。WebMCP 的出发点,就是让“网页自己决定自己能提供哪些工具”,Agent 实时去读一份“能力说明书”,而不是靠开发者在两套系统之间来回同步。

1.2 WebMCP 跟 MCP 是什么关系

MCP(Model Context Protocol)现在已经被很多大模型生态接受,它把“模型”和“外部工具”解耦,工具方只要实现一套协议,客户端就能统一调用。WebMCP 这个叫法并不是官方标准,我理解的是把 MCP 的思想落地到网页场景里:网页作为工具提供方,暴露一个 manifest 入口,里面写清楚有哪些工具、每个工具的参数格式、调用地址;AI Agent 作为调用方,先读 manifest,再按规则去调用。本质上就是一个“网页版的工具开放协议”。

跟传统后端 API 相比,WebMCP 更强调“页面状态”与“业务动词”的表达。普通 API 返回的是数据,WebMCP 返回的是带页面上下文的结构化结果;普通 API 需要开发者逐个对接,WebMCP 则希望 Agent 能自发现、自描述、自调用。拿点餐来打比方:传统 API 是你直接进后厨说“给我炒一个宫保鸡丁”,WebMCP 是先给你一份菜单,你按菜单点菜,厨师按标准流程出菜,哪怕这家店是新开的,你也不需要重新学一套沟通方式。

1.3 适合用 WebMCP 的场景,以及不适合的场景

先说不适合的,避免大家一上来就套错地方。如果你的系统本身就是一组标准的内部 REST API,调用关系固定、权限模型简单,那直接用 Function Calling 对接就好,WebMCP 反而多了一层封装。低延迟数字运算、大规模数据流、音视频实时交互,也不适合走这一类 HTTPS 请求,应该用专门的 RPC 或 WebSocket 通道。

适合的场景主要有这么几类:第一,页面状态检查,比如 Agent 定期确认下单页的按钮是否可点击、公告栏是否正常展示;第二,自然语言驱动的页面操作,比如用户对 Agent 说“帮我把筛选条件重置一下”;第三,内部系统智能助手,运营后台、数据面板、CMS 编辑器这类页面工具非常杂乱,正好用 WebMCP 统一暴露;第四,知识库或内容页面的二次加工,比如从某个页面抽取结构化数据,再由 Agent 决定下一步动作。总结起来,只要“功能长在网页里、动作以页面状态为上下文”,WebMCP 就能发挥价值。

2. WebMCP 的核心设计:协议、结构与鉴权

2.1 一切从 manifest 开始:先给 Agent 一份“能力菜单”

我在设计 WebMCP 时,把 manifest 当成整个协议的心脏。Agent 第一次接入时,不需要提前知道你的页面有哪些功能,只需要访问一个固定地址,比如GET /mcp/manifest,拿到一份 JSON,里面写清楚协议版本、服务名称、支持的工具列表、鉴权方式。这样做最大的好处是解耦:页面功能升级,只要 manifest 同步更新,Agent 就能感知到,不需要重新发版。

下面是我在一套演示系统里实际用过的 manifest 结构:

{ "protocol": "webmcp", "version": "1.0", "server_name": "order-console-webmcp", "server_url": "/mcp", "auth": { "type": "bearer", "token_url": "/auth/token" }, "tools": [ { "name": "get_element_status", "description": "获取页面指定元素的可见性、可用性、文本内容,用于判断按钮或区域当前是否能操作。", "parameters": { "type": "object", "properties": { "element_id": { "type": "string", "description": "页面元素的唯一 ID,比如 submit-btn、order-list" } }, "required": ["element_id"] } }, { "name": "click_element", "description": "模拟点击页面上的指定元素,触发对应业务动作。只建议对按钮类元素使用。", "parameters": { "type": "object", "properties": { "element_id": { "type": "string", "description": "需要点击的元素 ID" }, "confirm": { "type": "boolean", "description": "是否需要二次确认,默认 false" } }, "required": ["element_id"] } } ] }

每个字段都不是随便写的。name是 Agent 在决定调用哪个函数时直接使用的标识,命名必须稳定,一旦发布了就别随便改;description是给大模型看的,要写清楚“这个工具是干嘛的、什么时候用、参数有什么限制”,因为模型读不懂代码,只能靠这段描述来判断要不要调用;parameters要符合 JSON Schema 规范,这样不同的 Agent 框架都能直接转成自己的工具结构,兼容性会好很多。

2.2 invoke 调用:极简接口,但返回结构要统一

manifest 只是“菜单”,真正干活的是 invoke 接口。我采用的路径是POST /mcp/invoke,请求体里带上session_idtoolparameterssession_id很重要,它让多次调用可以共享上下文,比如 Agent 先查了按钮状态,再点击按钮,服务端能知道这是在同一个“会话场景”里发生的,避免不同页面、不同用户的动作混在一起。

一个标准的调用请求长这样:

POST /mcp/invoke Authorization: Bearer <WEB_MCP_TOKEN> Content-Type: application/json { "session_id": "sess_001", "tool": "get_element_status", "parameters": { "element_id": "submit-btn" } }

对应的响应我建议统一成这样:

{ "request_id": "req_3fa9d81c", "code": 0, "tool": "get_element_status", "result": { "element_id": "submit-btn", "visible": true, "enabled": false, "text": "处理中..." }, "elapsed_ms": 128 }

request_idelapsed_ms看起来不起眼,排查问题时却帮了大忙。Agent 侧拿request_id去问服务端“我那次调用到底发生了什么”,一查一个准;elapsed_ms则让 Agent 感知到工具调用是不是出现了性能劣化。返回结果里的result我强烈建议只放结构化 JSON,不要把整段 HTML 或者一大段文本扔给模型。模型处理 JSON 比处理标签文本稳定得多,而且 token 消耗也更低。

2.3 鉴权与权限控制:别让 Agent 在页面上裸奔

我在早期做 WebMCP 对接时,注意力全放在功能上,鉴权就顺手写了个固定的 Header 字符串。后来真正接进业务系统才发现,这玩意儿的攻击面比你想象中大:Agent 能调用的工具,相当于有人拿着一张 API 通行证在操作你的页面。如果这个通行证长期有效、权限又不分粒度,一旦泄露,对方就能直接执行下单、改配置、删数据等危险操作。

目前的实践是三层控制:第一层,接口本身必须有鉴权,我推荐短期 token,可以用 JWT 或者简单的签名方案,有效期控制在 30 分钟到 2 小时,避免长期令牌泄露风险;第二层,工具级权限控制,manifest 里可以再加一个可选的"required_permission"字段,比如click_element需要order:write,调用时服务端校验角色权限;第三层,危险操作二次确认,比如点击“删除按钮”这类高风险动作,WebMCP 可以在响应中返回"need_confirm": true,要求 Agent 先跟用户确认再发起正式调用。

2.4 错误码规范:统一格式,让 Agent 能自动恢复

模型调用工具时经常会出现参数传错、目标不存在、权限不足等情况。一开始我的接口出错时直接抛 HTTP 500,或者返回各种不规则的错误文案,结果 Agent 看到异常信息就懵了,只能干巴巴地跟用户说“出错了”。后来我总结出一套错误码分段,用结构化错误响应替代裸异常:

code 区间含义示例
1xxxx参数错误10001 缺少必填参数,10002 参数格式不合法
2xxxx权限错误20001 未认证,20002 无权限调用该工具
3xxxx资源状态错误30001 元素不存在,30002 元素当前不可见
5xxxx服务端内部错误50001 内部执行异常

对应的响应结构如下:

{ "code": 10001, "message": "missing_parameter", "detail": "'element_id' is required." }

关键点在于detail要写得“对 Agent 友好”,最好带上排查建议,比如“请检查 element_id 是否拼写正确,当前页面可用的元素包括 submit-btn、order-list”。这样 Agent 收到错误后,能直接把细节内容作为上下文交给大模型,让模型修正参数重新调用,形成自动纠错闭环。

3. 从零实现一个带 WebMCP 的网页服务

3.1 技术选型:FastAPI + 内存状态,先把链路跑通

我给演示系统选型的原则很简单:能用最少的代码验证完整链路。后端用 FastAPI,是因为它对 Pydantic 校验、JSON 序列化、自动文档的支持都非常顺手,几行代码就能搭出一个符合规范的 API。前端我用一个原生 HTML 页面加少量 JavaScript,避免引入前端框架把示例复杂度拉高。状态存储先放在内存里,用共享字典模拟页面元素状态,等链路跑通了再替换成真实数据库或者浏览器自动化。

为什么先强调“跑通链路”?因为 WebMCP 最大的坑往往不是单个接口的写法,而是 Agent、网页、后端三者之间能不能闭环。先把玩具版本跑通,再往里面填真实业务,这个顺序能省下大量调试时间。我见过太多人一上来就追求生产级架构,结果一个月过去连一眼能从 Agent 发起到页面状态变更的完整请求链路都演示不了。

3.2 后端核心代码:manifest 和 invoke 的工程化写法

下面这段代码是我整理过的最小可用实现,已经可以复制下来直接跑。为了演示,我定义了两个工具:get_element_status查询页面元素状态,click_element模拟点击一个元素并产生状态变化。实际页面里,工具函数内部应该对接真实业务逻辑。

# webmcp_demo.py from fastapi import FastAPI, Header, HTTPException from pydantic import BaseModel from typing import Optional import time import uuid app = FastAPI(title="WebMCP Demo Server") WEB_MCP_TOKEN = "change-me-to-a-real-token" # 模拟页面里的元素状态,真实项目中这里应该对接数据库或实时页面状态 page_state = { "submit-btn": {"visible": True, "enabled": True, "text": "提交订单"}, "order-list": {"visible": True, "enabled": True, "text": "最近 3 笔订单"}, } TOOLS = [ { "name": "get_element_status", "description": "获取页面指定元素的可见性、可用性和文本内容。", "parameters": { "type": "object", "properties": { "element_id": { "type": "string", "description": "页面元素 ID,例如 submit-btn" } }, "required": ["element_id"], }, }, { "name": "click_element", "description": "模拟点击页面上的指定元素,触发对应业务动作。", "parameters": { "type": "object", "properties": { "element_id": { "type": "string", "description": "需要点击的元素 ID" } }, "required": ["element_id"], }, }, ] class InvokeRequest(BaseModel): session_id: str tool: str parameters: dict = {} def require_token(authorization: Optional[str]): token = authorization.replace("Bearer ", "") if authorization else "" if token != WEB_MCP_TOKEN: raise HTTPException(status_code=401, detail="invalid_token") def success_result(tool: str, result: dict, elapsed_ms: int): return { "request_id": str(uuid.uuid4()), "code": 0, "tool": tool, "result": result, "elapsed_ms": elapsed_ms, } @app.get("/mcp/manifest") def get_manifest(): return { "protocol": "webmcp", "version": "1.0", "server_name": "order-console-demo", "server_url": "/mcp", "auth": {"type": "bearer", "token_url": "/auth/token"}, "tools": TOOLS, } @app.post("/auth/token") def get_token(user: str = "demo"): # 这里只做演示,生产环境必须换成正式的身份认证,并控制 token 有效期 expires = int(time.time()) + 3600 token = f"demo-{expires}" return {"token": token, "expires_at": expires} @app.post("/mcp/invoke") def invoke(req: InvokeRequest, authorization: Optional[str] = Header(None)): require_token(authorization) tool = req.tool parameters = req.parameters start = time.time() if tool == "get_element_status": element_id = parameters.get("element_id") if not element_id: raise HTTPException(status_code=400, detail="missing_parameter: element_id") if element_id not in page_state: raise HTTPException(status_code=404, detail="element_not_found") return success_result(tool, page_state[element_id], int((time.time() - start) * 1000)) if tool == "click_element": element_id = parameters.get("element_id") if not element_id: raise HTTPException(status_code=400, detail="missing_parameter: element_id") if element_id not in page_state: raise HTTPException(status_code=404, detail="element_not_found") # 模拟点击之后的副作用:提交按钮变成不可用,文案改变 if element_id == "submit-btn": page_state["submit-btn"]["enabled"] = False page_state["submit-btn"]["text"] = "处理中..." return success_result(tool, {"clicked": True, "state": page_state[element_id]}, int((time.time() - start) * 1000)) raise HTTPException(status_code=400, detail="tool_not_found")

这套代码里我特意把 token 演示简化成了固定字符串,代码里那段/auth/token只是示意。真实项目里建议换成一个成熟方案,比如签发短期 JWT,exp设置为当前时间加 2 小时,同时把密钥放到环境变量里。另外,page_state这个内存字典在真实系统中要替换成:

  • 如果页面本身就是后端渲染,状态以数据库为准;
  • 如果页面是前端 SPA,由前端通过事件接口把状态同步到后端;
  • 如果要控制真实浏览器,可以用 Playwright 或 Selenium 在服务端执行页面操作。

3.3 前端页面如何跟 WebMCP 保持状态一致

很多朋友会卡在“Agent 改了状态,页面上看不到”“页面上的操作,Agent 查不到”这类问题,本质上是状态源不一致。我在演示里把所有状态统一收口到page_state,前端页面加载时通过一个状态接口渲染,Agent 调用 WebMCP 后直接修改同一个状态源,页面再用轮询或 WebSocket 拿到最新状态。下面是我给页面写的最小示例:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>订单控制台</title> </head> <body> <h1>订单处理页面</h1> <button id="submit-btn">提交订单</button> <div id="order-list">最近 3 笔订单</div> <script> async function syncState() { const res = await fetch('/api/state'); const state = await res.json(); const btn = document.getElementById('submit-btn'); btn.disabled = !state['submit-btn'].enabled; btn.textContent = state['submit-btn'].text; document.getElementById('order-list').textContent = state['order-list'].text; } document.getElementById('submit-btn').addEventListener('click', async () => { await fetch('/api/order/submit', { method: 'POST' }); syncState(); }); setInterval(syncState, 3000); syncState(); </script> </body> </html>

在这个架构里,“页面元素”和“后端状态”之间不是两个孤立世界,而是由同一套状态来驱动。页面用户点了按钮,接口会改状态;Agent 调用 WebMCP 点击按钮,也会改状态。无论谁来操作,页面上都能同步看到结果,Agent 拿到的状态也不会过期到离谱。这么做可能不是最高性能的方案,但对中后台系统、运营工具这类场景,简单可靠比极致实时更重要。

3.4 参数选择与边界条件:超时、并发、幂等

任何工具接口都不能无限信任调用方,尤其是 Agent 这种“非确定性调用方”。我在实现 WebMCP 时,对三个边界条件做了强约束。

超时控制上,单工具执行时间最长不超过 30 秒。Agent 侧的超时时间要留足余量,我一般设为 45 秒。如果一个工具确实需要执行超过 30 秒,不要死等,先返回"status": "pending",再通过 Webhook 回调或者让 Agent 轮询结果。并发控制上,同一个session_id内的调用我默认串行执行,防止两个工具同时操作同一个页面元素导致状态错乱;不同会话之间的调用可以并行。幂等控制上,凡是会产生副作用的写操作,请求体里都要支持request_id,服务端记录已执行过的request_id,重复请求直接返回上一次的结果,不会重复触发业务动作。这一点在做“提交订单”这类工具时尤其重要,Agent 一旦遇到网络超时大概率会重试,没有幂等防护,一单被提交两次的教训可是真金白银换来的。

4. 把 AI Agent 接进来:一次完整的调用实弹

4.1 Agent 如何发现 WebMCP 工具:动态发现替代硬编码

Agent 跟 WebMCP 对接的方式有两种。一种是把 manifest 里的工具手动翻译成 Agent 代码里的函数,适合工具数量少、结构固定的场景;另一种是动态发现,Agent 启动时先请求 manifest,然后动态构建工具列表,适合工具经常变更的场景。我更推荐第二种,因为 WebMCP 的核心优势就是“网页自动描述能力”,你再用手动翻译,等于把这个优势丢掉了。

下面是用 OpenAI Function Calling 风格的 Python 代码来动态构建工具的思路,换成其他大模型平台也差不多:

import requests from openai import OpenAI client = OpenAI() def load_webmcp_tools(server_url): resp = requests.get(f"{server_url}/mcp/manifest", timeout=10) resp.raise_for_status() manifest = resp.json() tools = [] for tool in manifest["tools"]: tools.append({ "type": "function", "function": { "name": tool["name"], "description": tool["description"], "parameters": tool["parameters"], } }) return tools server_url = "http://127.0.0.1:8000" webmcp_tools = load_webmcp_tools(server_url)

这里需要注意,manifest 里的parameters已经是 JSON Schema 格式,直接塞给主流平台都能识别,不需要再转结构。如果你用 LangChain,也可以把工具包一层@tool装饰器,然后把它加入 Agent 的工具列表。

4.2 跑通一次真实调用链:从用户问题到页面状态变更

为了让你更直观地理解完整流程,我给你还原一次实操记录。假设用户对 Agent 说:“检查一下订单提交按钮是否可用,如果状态不对,帮我重置一下。”

第一步,Agent 看完用户请求后,通过 Function Calling 选择了工具get_element_status,调用参数是{"element_id": "submit-btn"};第二步,WebMCP 服务端返回{"visible": true, "enabled": false, "text": "处理中..."};第三步,Agent 发现按钮不可用,认为这是一个异常状态,于是决定调用click_element看能否触发一次重试,WebMCP 返回{"clicked": true, "state": {"visible": true, "enabled": false, "text": "处理中..."}};第四步,Agent 综合两次调用结果,向用户输出结论:“提交按钮目前不可点击,按钮文本是‘处理中’,说明系统里已经存在一笔正在处理的订单,我没法直接重置。”

这个过程中最让我惊讶的一点是,Agent 并没有被“重置”这个动词带偏,它通过页面状态判断出重置是不合理的动作,从而拒绝执行。这正好说明了“结构化状态反馈”对 Agent 的价值。如果 WebMCP 返回的是一段杂乱的 HTML,模型大概率会瞎猜:可能直接说按钮正常,也可能说点击成功,但不会给出这么有分寸的判断。

5. 常见问题与排查技巧实录

5.1 Agent 调用超时,链路卡住怎么办

这是最常见的坑。先区分是“接口真的慢”还是“Agent 那边超时配置太短”。我在第一步会先用 curl 手动调用一次 WebMCP,看响应时间。如果接口本身耗时小于 200ms,那问题基本出在 Agent 侧的超时配置,把工具调用的timeout调大即可;如果接口真的慢,就需要在工具内部做“快速失败”,比如查数据库超过 5 秒就返回超时错误,不要让整个 HTTP 请求挂在那里占住 Agent 的上下文窗口。

注意:给 Agent 做工具调用时,宁可让它快速看到一个明确的错误,也不要让它长时间无响应。大模型没有耐心,很多 Agent 框架遇到响应超时会直接放弃,后面再重试也不会,结果就是你看着日志发呆。

5.2 页面状态跟后端状态对不上

我踩过的最大一个坑就是:前端用户操作改了数据库,但page_state还是旧值;Agent 调用 WebMCP 拿到旧状态,给出的判断完全错误。后来我把所有页面状态都改成“从唯一数据源读取”,也就是数据库查询结果经过一次格式化后直接返回,不再维护独立的缓存快照。如果确实需要缓存,一定要加last_sync_at时间戳,并在返回给 Agent 时明确标注“该状态是 10 秒前的快照”,让 Agent 有判断空间。

5.3 危险工具被 Agent 误触发

这个问题在演示环境里不痛不痒,生产环境里会出事。比如用户说“帮我把所有订单删除”,Agent 如果只有一个delete_order工具,它可能真的会逐个调用。我的方案是:第一,工具命名不要过于泛化,用delete_order_with_confirm代替delete_order;第二,manifest 里给危险工具增加标记,比如"danger_level": "high";第三,Agent 框架里对高危险工具做强制人工确认,确认前不发起调用;第四,服务端对写操作统一记录审计日志,谁调用了、参数是什么、结果是什么,全都留下来。

5.4 常见问题速查表

现象可能原因处理建议
Agent 拿到的 manifest 是旧的Agent 侧或浏览器有缓存加上?v=2版本参数,Agent 端缓存控制在 60 秒内
工具调用一直返回 401token 过期或没正确传 Header检查 Authorization 前缀是否包含Bearer,确认 token 有效期
元素一直提示不存在页面结构变化,元素 ID 被改建立元素 ID 映射表,在 manifest 中同步更新
Agent 把参数传成中文模型对英文参数名理解不到位加强description中的示例,比如element_id: "例如 submit-btn"
多个 Agent 会话互相干扰没有隔离会话状态确认每个请求都带唯一session_id,服务端按会话隔离状态
点击工具执行了两次网络重试导致重复请求在 invoke 请求里增加request_id,服务端做幂等去重

6. 我的落地经验与后续扩展

把这套 WebMCP 方案在几个小型项目里跑过之后,我的体会是:它不太像一个严格的标准规范,更像我手里一套组织“网页能力”的方法论。真正要落地,三点建议供你参考。

第一,一定要让 WebMCP 反映真实页面状态,不要 mock 一套状态再跟页面脱节,否则 Agent 再聪明也会被脏数据带偏。第二,从只读工具开始接。先让 Agent 能查状态、查列表,验证整个链路稳稳当当,再逐步放开点击、提交、删除这些写操作。全读写一步到位,出问题的时候你连锅都甩不干净。第三,日志和审计要前置。每个 WebMCP 调用在线上环境都要能看到是谁调用的、调用了什么、结果如何,这既是安全底线,也是出问题后快速定位的救命稻草。

这个方向后续可以延展的地方也不少。比如把 WebMCP manifest 转成标准 MCP server,这样所有支持 MCP 协议的客户端都能直接使用;再比如给长耗时工具增加 Webhook 回调能力,Agent 提交任务后不用一直等,任务完成由服务端主动通知;还可以做一个可视化工具编排界面,在页面上点选按钮、圈一下区域,自动生成能力描述,让非技术同学也能维护 Agent 的工具清单。

最后分享一个调试小技巧:在你准备把 Agent 接进来之前,先用 curl 把自己当成 Agent,把全套请求手动打一遍:

curl -s http://127.0.0.1:8000/mcp/manifest | python3 -m json.tool curl -s -X POST http://127.0.0.1:8000/mcp/invoke \ -H "Authorization: Bearer change-me" \ -H "Content-Type: application/json" \ -d '{"session_id":"debug-001","tool":"get_element_status","parameters":{"element_id":"submit-btn"}}'

能看到结构化响应,再让 Agent 也走一遍同样的链路,基本就能把“Agent 的问题”和“WebMCP 服务的问题”切分开。踩过几次坑之后,我越来越相信一件事:很多时候不是模型不够懂你的业务,而是你的业务还没有给模型开一扇门。WebMCP 就是我想象中那扇门的样子——网页把自己的工具大大方方交给 Agent,Agent 也终于能从“能说会道”变成“能干活”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 5:26:38

ACTF2020 Upload 1题解:文件上传绕过与蚁剑连接实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 5:25:11

APK Editor Studio中文版:零命令行修改APK资源与签名

简介&#xff1a;APK Editor Studio 1.4.0 中文多语免费版是一款面向Android开发者、逆向工程师及进阶爱好者的轻量级APK反编译与编辑工具&#xff0c;专为快速修改应用图标、标题、资源、权限、清单文件及签名安装等场景设计&#xff0c;无需深入Smali或Java代码即可完成常见A…

作者头像 李华
网站建设 2026/9/10 5:24:50

Java网络爬虫实战:从环境搭建到多线程采集

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 5:24:44

DeepSeek实战:从JSON到PPT的AI生成器开发指南

这个项目其实是从一个特别日常的需求冒出来的——给部门做季度汇报PPT&#xff0c;光调格式就花了一晚上。后来我就想&#xff0c;能不能让DeepSeek直接帮我把整个PPT的结构、文案甚至排版思路都生成好&#xff0c;我只需要做最后的微调&#xff1f;于是就有了这个用DeepSeek搭…

作者头像 李华
网站建设 2026/9/10 5:23:50

SpringBoot+Vue前后端分离电影购票系统毕设开发指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华