apic
apic
一个应用转 API 编译器。 把它指向一个没有面向 agent 的 API 的 Web 应用。一个 computer-use agent 会探索 UI,通过执行来验证它发现的内容,并为该应用生成一个类型化的 MCP 服务器。
Playwright MCP 每次调用都解释应用。apic 只编译一次。
在伦敦 2026 年 8 月 22 日的 {Tech: Europe} × VEED 黑客松 上,由一人用一天时间独立完成。
→ apic-ui.vercel.app — 演示视频在那里,还有每个合作模型做了什么决定,以及编译器测量了什么。
公共消费类网站
apic --read https://example.com 将任何消费类网站的公共只读表面编译成 MCP 工具。它从该网站开始(加上可选的同站种子),发现搜索框、过滤器和重复的结果卡片,然后只生成那些行能通过冷重放的工具。它不假设 Deliveroo 的路由、餐厅词汇、账户、购物车或结账流程。
对于已知的集合/条目页面,将其作为同站直接种子显式传入:APIC_READ_DIRECT_URL=https://example.com/catalog/item apic --read https://example.com。agent 提供的 URL 被限制在编译进配方中的源站内。
一个提示词,无需目标 URL
当 APIC 作为其 MCP 服务器连接时,对于普通的消费类问题,使用 fulfill_request 而不是 compile_app:
{ "request": "Find me the cheapest pizza near 17 & 18 Clere Street" }服务器使用 Tavily 查找公开的候选服务,使用 OpenAI 选择并操作编译后的流程,使用 h 来优先处理模糊的读取控件,使用 Pioneer 来分类探测是否产生了有意义的结果,并且仅在该分类需要视觉裁决时使用 fal。如果某个候选被挑战或没有可重放的公共流程,它会尝试一个小的、源站不同的回退集。它从不登录、下单、结账或绕过挑战。存活下来的工具经过冷验证,作为证据返回,并在同一个 MCP 服务器上注册以供后续调用。
Related MCP server: mcp-apps-demo-engine
演示
在网站上观看:apic-ui.vercel.app — 两分钟,未剪辑:编译过程、生成的工具出现在实时会话中,以及 watcher 自行捕获 UI 变化。
同一个网站还承载了本 README 报告的数字、每个合作方的细分,以及每个 MCP 客户端的安装片段。
问题
Computer-use agent 在经济上无法规模化。每次运行都从像素中重新推导相同的知识:每一步一次模型往返,每一步一个页面快照填满上下文窗口,以及可靠性在链条上不断向下累积。这就是为什么它们被不断演示却很少部署。
软件 agent 最需要驱动的,恰恰是最不可能提供 API 的软件——内部工具、遗留系统、任何供应商已消失的东西。你无法嗅探一个什么都没有的网络标签页,也无法要求一个 2011 年的业务应用采用新协议。
apic 只使用昂贵的 agent 一次,来编写接口。之后它就是一个函数调用。
工作原理
阶段 | 作用 | 技术 |
Ground | 读取目标自身的文档,学习该应用的词汇,因此词汇不是硬编码为 Vikunja 的 | Tavily + OpenAI,按主机缓存 — 仅 CLI 路径 |
Explore | 驱动应用,对可操作项排序,让创建操作优先,打开表单并提交 | Playwright + h(针对词汇无法命名的控件的升级层) |
Perceive | 判断是否有任何有意义的变化 | DOM diff,在 CLI 路径上升级到 fal |
Synthesise | 将轨迹转化为类型化的工具模式 | 确定性 — 无模型调用 |
Verify | 用应用从未见过的参数冷重放工具 | 无密钥 diff 下限,然后是微调的 Pioneer 评判器,OpenAI 待命 |
Emit | 编写一个可运行的 MCP 服务器、其模式及其证据 | — |
Watch | 按间隔重新运行测试套件 | — |
Heal | 红色工具在其自身种子处重新进入发现流程 | — |
修复路径就是构建路径。 修复不是修补选择器——它重新运行最初发现该工具的发现流程,并按名称合成产生的名称进行匹配。重命名的按钮仍然产生 createProject。
只有当应用确认了写入时,工具才存在
统计 DOM 节点数量会为页面上的每个按钮产生一个看起来合理的工具。apic 仅在应用自身断言状态发生变化时才生成工具,通过三个信号覆盖三种不同的应用行为:
行为 | 示例 | 信号 |
宣布并停留 | 创建标签 | 状态区域中的成功横幅 |
宣布并导航 | 创建项目 | 横幅在 URL 变化后仍然存在 |
静默追加 | 看板快速添加 | 提交的值作为渲染内容出现 |
迁移 | 将卡片拖到列之间 | 卡片改变了容器 |
迁移很重要,因为拖拽没有横幅,也不会回显任何内容——卡片已经存在。包含关系的变化就是证据,任何外观上的重新渲染都无法产生它。
配方绑定到身份,而不是位置
Vikunja 在每次页面加载时重新生成元素 id,因此存储的选择器一到达就失效了。配方记录一个字段是什么——它的标签、占位符、名称——重放时实时重新解析它,回退到稳定优先的选择器链(name → 稳定 id → 占位符 → 最后才是生成的 id)。
结果
从 UI 编译而来。编译期间从不读取目标的 OpenAPI 规范——它仅用作评分的基准真值,这就是为什么召回率这个数字有任何意义。
分母,先说清楚: 18 是 Vikunja 自己的 OpenAPI 规范中 /projects、/tasks 和 /labels 上的每个写操作(POST/PUT/DELETE),在移除不是看板手势的内容之后——团队、项目级权限、链接共享、附件、任务关系、复制、批量端点和已读回执。Vikunja 总共发布了 105 个写操作;18 是个人在看板(Kanban board)上可以执行的子集,每个生成的工具最多只能声称其中一个,因此召回率不能通过宽松匹配来虚增。
RECALL 8/18 of the board write-ops in the target's own API
PRECISION 9/9 emitted tools that map to a real operation
VERIFIED 9/9 survived a cold replay with arguments never seen before发现九个工具,提供九个。被拒绝的工具不会被删除——它们保留在 tools.json 中,带有 verified: false,因为被拒绝的工具是关于编译器的证据,而不是垃圾。
markTask 不稳定,这比 9/9 更有价值。 对同一个 bundle 连续两次验证运行,中间没有变化,给出了 8/9 然后是 9/9:它先以 "观察到变更但没有任何东西确认写入" 失败,然后以 "成功——任务已成功保存。" 通过。可能的原因是种子任务的状态——落在一个已完成的任务上,控件会显示为 MARK AS UNDONE 并以不同方式确认。它不接受参数,因此也无法通过参数来消除歧义。
这是下面列为未解决的不稳定与漂移问题的一个活实例:watch 会把那次失败计为漂移并调用 heal,而实际上根本没有漂移。
一个活跃下午的持续验证:
327 checks · 118 breaks · 3 automatic repairs · MTTR 20s(out/watch-stats.json,从 11:33 BST 开始的 38 个周期,写这篇文章时仍在运行——计数器在移动。)
按它本来的样子来读那个中断计数。stats.breaks++ 在每个周期的每次红色重放时触发,因此三个在 38 个周期中保持红色的工具读起来像 ~114 次中断——这是一个红色工具周期计数,而不是 118 个独立的漂移事件。而且这个 watcher 是在 11:33 启动的,早于对 heal() 的修复,该修复返回了一个修复后的配方,但没有 replay() 的开启器实际点击所需的新的 provenance;因此一个控件被重命名的工具在每个周期都被修复,却没有一个周期变绿。这就是 6/9 的大部分。已在代码中修复,没有在可比的时间窗口内重新收集数据。
合作技术
每个技术都有一个阶段,并且每个都是降级而不是阻塞:整个流水线在完全没有 API 密钥的情况下运行,保真度降低。这个特性就是为什么编译器在任何凭据到达之前就可以构建——也是为什么一个集成可以停止贡献而编译过程却注意不到,这正是状态列所记录的。
技术 | 阶段 | 为何占有一席之地 | 状态 |
OpenAI | 验证 | 关于预测效果是否发生的独立裁决,叠加在无密钥的 diff 基线之上。它可以维持拒绝,但绝不会推翻拒绝 | 使用中 — 它已对 |
fal | 感知 | 快速 VLM,用于判断实质性改动与外观性改动;仅在 DOM diff 不明确时升级调用 | 使用中 — 上次编译中 4/4 个升级步骤由它判定,其中 2 个被裁定为外观性改动。仅限 CLI 路径; |
Pioneer | 验证、蒸馏 | 基于 apic 自身验证证据微调的 GLiNER2 编码器取代了 GPT-4.1-mini 评判器 —— 并在留出工具上胜过它(见下文)。同时也是 | 使用中 — 已设置 |
h | 探索 | 读取页面,并指出无密钥词汇表拒绝的写入操作 | 使用中 — 对剩余控件每个 seed 运行一次;在 Vikunja 上正确地将 3 个中的 0 个命名为写入操作 |
Tavily | 接地 | 应用文档 → 领域词汇,因此工具被命名为 | 使用中 — |
两级拆分正是该产品自身论点的自我应用:fal 是廉价的高频感知层,OpenAI 是昂贵的低频推理层。 出现失败才升级,而不是每次调用都升级。
每个组件的实际调用方式
h — holo3-1-35b-a3b、api.hcompany.ai/v1(兼容 OpenAI)。
gesture() 使用正则表达式将控件的可见文本映射为 <verb, resource> 对,对其它一切返回 null。这个 null 是精度的闸门,但也正是召回流失的地方:一个仅图标的按钮、一个不以动词开头的控件、或一个措辞从未被词汇表预见的应用,无论其书写多么直白,都会被丢弃。h 正是为这一集合而设的升级层——discover.js 的 classify() 会在每个 seed 发送一次页面 JPEG 和被拒绝的控件,并询问其中哪些会执行写入。
有三点阻止它损害精度。答案会由 plan.gestureFrom() 依据封闭词汇表——六个动词、四个资源——进行验证,因此一个虚构的动词无法为工具命名。切分之外的控件会被扣下而不是提交,因为排除 ADD TO FAVORITES 是范围决策,而不是留给模型填补的缺口。并且,一个被分类的控件仍必须像其它候选控件一样让应用确认一次写入。
实测数据,取自本 README 报告的编译: h 读取词汇表在 Vikunja 任务页上留下的三个未解析控件,并将其中一个命名为写入操作——一个正则表达式直接丢弃的纯图标控件:
! h read 3 unresolved controls, named 1
! h: "Kanban bucket: To-Do" -> move task (Pencil icon allows changing task status)这就是升级层在履行它存在的意义:一个没有引导动词、没有可用文本的控件,从其图标中被识别出来,并映射进封闭词汇表。
它没有新增一个工具,我们也不声称它新增了。 那时 move task 已被发现过两次——一次通过看板拖拽(Move card between columns),一次通过任务页的分组下拉框(Kanban bucket: Doing)——因此 h 的答案去重进了拖拽生成的 moveTask。在这个目标上,h 是佐证,而不是召回:它是通往一个另外两条路径早已触达的动作的第三条独立路径。本文件的一个早期修订版曾说 h 从未被触达且未命名任何控件;两种说法都是错的。
h 是否增加了召回,在此处未经测试,因为 Vikunja 的写入操作标签异常清晰。它为之设计的场景——按钮全是图标的应用——恰恰是这个目标没有呈现的场景。没有这一关键,编译只会失去那份佐证,其它一切不受影响。
fal — google/gemini-2.5-flash-lite,经由 fal-ai/any-llm/vision。
DOM 差异比较器只说明页面是否发生变化。它无法判定文本未描述的变化——例如一张卡片换了列,或一个控件只是亮了起来。perceive.js 的 adjudicate() 会将这些步骤(且仅这些步骤)升级为像素级判断。
实测数据,取自上次完整编译: vision: 4/4 escalated steps judged by fal, 1 drag corroborated, 2 found cosmetic。两个外观性裁决是有趣的那一半——fal 移除了那些否则会被当作写入操作来探测的候选。它从 cli.js 运行;通过 MCP 服务器上的 compile_app 驱动的编译不会升级。
OpenAI — gpt-4.1-mini,结构化输出。
verify.js 使用应用从未见过的参数冷重放每个已发出的工具,并对结果进行两次评判:先是确定性的 diff 基线,然后是模型。模型可以维持拒绝,但绝不可能推翻拒绝——diff 无法确认的工具,无论评判者多么自信,都保持被拒绝状态。
实测: 在 markTask 失败的那次运行中,它的记录为 openai/gpt-4.1-mini disagreed but cannot overturn a rejection。这种不对称是有意为之:一个可以把自己猜测提升为结论的评判者就是精度漏洞。
Pioneer — GLiNER2(fastino/gliner2-base-v1),每一步一次 POST /inference。
distill.js 将每一步的 diff 文本放在各自独立的请求中发送,并在 0.6 置信度阈值之上得到一个状态变化类别、一个破坏性标志以及领域名词。它过去曾将整个轨迹批量发送,而批量正是下面第三条来之不易的经验教训所讲的内容:同一文本单独评分时 creation 为 0.777,在位置 0 时为 creation 1.000,而在反转批次的第 2 个位置时为 DELETION 0.600——一个错误标签越过了阈值。PIONEER_MODEL 中一个已完成的训练任务 ID 会把基础编码器替换为在 apic 自身标签上微调的检查点——即系统在编译自己的感知层——其它一切不变。
Pioneer — 微调后的验证评判器。 这是 Pioneer 支线挑战的参赛条目:微调一个优于或取代通用 LLM API 调用的模型。 它所取代的调用是 verify.js 中的 judgeModel()——GPT-4.1-mini、200 词的系统提示、结构化输出、对每个重放工具问一个问题:给定这个 DOM diff,预测的写入是否被证实发生了? 这本质上是套着聊天补全外衣的两标签文本分类。
pioneer-train.js 完全从产品自身的运行产物中构建替代模型,无需人工标注:
collect(收集) —— 通过
verifyAll()用全新参数将每个已编译工具重放六次,记录证据以及随产品发布的评判器(diff 基线 + GPT)给出的裁决。共 54 行真实数据。dataset(数据集) —— 通过删除基线所依赖的证据来派生负样本(banner 消失、回显被移入输入它的输入框、参数未填充、重放抛出异常、没有任何变化),并通过保留标签来派生正样本(节点顺序反转、添加无关节点、参数改写成真人会输入的取值)。每个派生行都由同一个确定性基线重新标注。共 788 行;按工具留出,因此基准测试衡量的是编码器从未见过的工具。
upload / train(上传/训练) ——
POST /felix/datasets/upload/url→ presigned PUT →POST /felix/training-jobs,fastino/gliner2-base-v1,LoRA,12 个 epoch。训练约四分钟。bench(基准) —— 让留出的行分别通过两个评判器。LLM 通过未修改的
judgeModel()调用,因此它看到的与生产环境完全一致。
评判器 | 准确率 | 精确率 | 召回率 | 假阳性 | 假阴性 | 毫秒/行 |
Pioneer GLiNER2 fine-tune(任务 | 94.4% | 100% | 87.6% | 0 | 12 | 150 |
OpenAI GPT-4.1-mini | 89.3% | 84.3% | 93.8% | 17 | 6 | 890 |
215 行留出数据,两个工具(createTask、assignLabel)未参与训练。编码器牺牲了一些召回率,换来了零假阳性——对这位评判者来说这是正确的取舍,因为它的设计是只能维持拒绝,绝不能提升猜测。将 PIONEER_JUDGE_MODEL 设置为任务 ID,verify 便会使用它;OpenAI 作为后备保持待命,而即使完全没有密钥,基线依然会运行。
有三条付出代价才学到的经验,全部已在线验证并记录在代码中:在分类规范里设置 multi_label/top_k 会使统一的 /inference 路径对所有文本返回 categories: [](这正是 distil 阶段整个上午没有任何输出的原因,而不是额度问题);GLiNER2 只能以 LoRA 方式训练——training_type: "full" 会被接受,然后在 Modal 内部失败且不留下任何日志行;对微调模型做批量推理(text: [...])返回的标签与输入无法一一对应,因此评判器每个请求只发送一条文本。
Tavily — api.tavily.com/search,返回五个结果,并包含答案。
ground.js 在首个 seed 之前运行。plan.js 内置了 Vikunja 的名词——bucket、task、label、project——而当 gesture() 被问到 issues 和 repositories 等任何其它内容时,由于表从未听说过它们,它会返回 null,控件即被丢弃。Tavily 获取目标自身的文档;OpenAI 在严格 schema 下将这些文字整理成封闭名词集;每个词都通过 /^[a-z][a-z-]{1,18}$/ 校验,上限为 12 个,并且合并进内置表而不是替换它,因此接地只能增加词汇,绝不会夺走 Vikunja 的词汇。在 .apic/ 下按主机缓存,所以重复编译零开销,演示也不依赖场地 wifi。
它会以三个步骤降级——没有 Tavily 密钥,就没有证据;没有 OpenAI 密钥,证据就无法被结构化;没有任何东西通过验证——每一步都会记录日志并让内置表保持原样。与 fal 一样,它从 cli.js 运行:MCP 服务器上的 compile_app 使用内置词汇表。
因此,上面的召回数据是在无密钥状态下产生的,升级的感知步骤由 fal 处理,验证流程由 OpenAI 评判器处理。它们并非完整合作伙伴技术栈的展示,本 README 也不会假装是。
设置
git clone https://github.com/brwbo/apic && cd apic
npm install && npx playwright install chromium
cp .env.example .env # fill in keys; .env is gitignored
npm run setup # starts the target app, checks every credential目标应用(自托管、一次性使用——切勿将其指向第三方产品):
docker volume create vikunja-files
docker run --rm -v vikunja-files:/data alpine sh -c "chown -R 1000:0 /data"
docker run -d --name vikunja -p 3456:3456 -v vikunja-files:/app/vikunja/files \
-e VIKUNJA_SERVICE_PUBLICURL=http://localhost:3456 \
-e VIKUNJA_DATABASE_PATH=/app/vikunja/files/vikunja.db \
-e VIKUNJA_RATELIMIT_ENABLED=false \
vikunja/vikunja:latest命令 | 作用 |
| 哪些凭据可用,哪些目标在线 |
| 探索 → 综合 → 生成 |
| 冷启动重放每个工具;只有存活者才会被提供 |
| 持续验证并自动修复 |
| 针对目标真实 API 的召回率与精确率 |
| 将 apic 本身作为 MCP 服务器运行——见下文 |
每条命令都读取相同的两个变量,因此整个运行过程可以指向一个 替代 bundle,而无需触碰线上那个:
APIC_OUT_DIR=out/rescue APIC_APP=vikunja npm run verify变量 | 默认值 | 含义 |
|
| 编译后的 bundle 存放位置。 |
|
| 其中使用哪个 bundle |
|
| 正在被编译的应用 |
|
| 目标的凭据 |
| 自动发现 | 仅当登录表单不在 |
|
| 开始探索的页面 |
种子与目标均由环境变量驱动——编译器中没有任何内容知道 Vikunja 的路由:
APIC_APP=gitea TARGET_URL=http://localhost:3001 APIC_SEEDS=/repo/create,/issues npm run compile生成的输出
generated/vikunja/ —— 服务器、schema,以及每个工具的
证据。该目录中没有任何内容是由人类编写的。
将其用作 MCP 服务器
claude mcp add apic -- node /path/to/apic/src/server.jssrc/server.js 以一个工具 compile_app 启动。将其指向一个 URL,
它就在进程内运行整个流水线,生成 generated/<app>/,将编译后的工具
注册到自身上,并发送 notifications/tools/list_changed——因此
这些工具在同一连接上即可被调用,无需重启。从冷启动开始:
[apic] ready - 0 compiled tools + compile_app
BEFORE compile, tools/list = [ 'compile_app' ]
compile_app returned in 22.1s
list_changed notification: YES
AFTER compile = [compile_app, createProject, createLabel, updateLabel, createTask]
createLabel -> {"ok":true,"effect":"creation","expected":"creation"}一个需要你重启它刚刚扩展的东西的编译器,只是一个构建步骤。 不需要重启的,才是实时编译器。客户端兼容性说明和完整记录: docs/mcp-client.md。
两种死胡同都以“回答”而非“报告”收场
客户端只在缺少某些东西的那一刻才会遇到 apic,而这两种时刻 过去都以对话结束而告终。
一个不存在的工具会返回能够创建它的编译结果:
unknown tool: createIssue
No compiled tool exposes that action (compiled so far: vikunja). If the app has no API for it, make one:
compile_app { "url": "http://localhost:3456", "goal": "createIssue" }一个应用已将其从脚下移走的工具会在调用路径上被修复——
watch 按定时器修复,服务器按需修复,都通过同一个
heal()。工具变红,编译器重新探索那一个动作,
修复被写回 tools.json,并且在调用方看到失败之前重试调用:
[apic] createLabel is red (no control matched "ADD LABEL (RENAMED)") - re-exploring to heal it
[apic] createLabel healed in 13.6s (click "…" -> "create label"; selectors re-resolved); retry passed
{ "ok": true, "effect": "creation", "healed": { "ms": 13589, "persisted": true } }一个健康的工具不会受到这些的任何影响:同样的调用,4.4 秒,无需重新探索。
相关工作,以及本项目的不同之处
项目 | 作用 | 差异 |
将浏览器自动化封装为类型化 MCP 工具 | 通用动词( | |
从 Actor 输入 schema 自动生成类型化工具 | Actor 是人工编写的——它从人工编写的契约生成包装器 | |
URL + 自然语言 → 结构化数据 | 返回数据而非接口,并且每次调用都重新运行 LLM | |
URL/HAR/OpenAPI → CLI + MCP 服务器,带验证门控 | 嗅探网络流量——应用必须已经拥有 API。apic 驱动的是 UI | |
OpenAPI 规范 → MCP 工具 | 要求 API 已经存在 | |
智能体按任务生成并复用 MCP | 通过搜索网络生成工具。apic 通过操作软件本身来推导工具 | |
页面在 JavaScript 中声明自己的工具 | 要求应用开发者采用它 | |
编写技能、验证、存储、复用 | 验证后保留循环的鼻祖 |
这个循环并不新颖——能力来自哪里才是新颖之处。 Alita 读取 互联网来制造工具;cli-printing-press 读取网络;Easy MCP 读取 规范。apic 读取的是应用本身。
目前尚不完善之处
直说无妨,因为一个隐藏自身失败模式的编译器算不上编译器。
h 在路径中,但在此目标上毫无贡献。 它读取了词汇表拒绝的 控件,并且正确地没有命名其中任何一个,因为 Vikunja 的看板切片 已被正则覆盖。升级是真实且可测量的;但在这里收益为零, 而一个控件是图标而非动词短语的目标,才是能体现其价值的场景。
compile_app运行的是精简流水线。compile.js是 MCP 服务器调用的进程内编译,它是cli.js减去五样东西: 接地(Tavily/OpenAI)、种子发现、专用表单页探测、 任务详情种子和看板拖拽,外加 fal 的视觉层——adjudicate()仅在cli.js中运行。它保留了发现、持久化、 Pioneer 蒸馏、综合和生成。这就是为什么上面的记录显示四个工具 而npm run compile产生九个:实时编译器演示和 9 工具 bundle 是两条不同的路径,只有 CLI 那条才是召回率数据所描述的。Pioneer 整个上午看起来都不可用——先是
403 payment_method_required,换了新 key 之后,每次调用都返回categories: [],代码将其解读为“没有意见”并回退到启发式。 第二个是请求形状的 bug(multi_label/top_k),不是 API 的问题。 每个集成都被设计为静默降级,而每个也确实如此——降级是 预期行为;但整整一个上午都没注意到,这就不对了。微调后的评判器只见过一个应用。 它的 788 行训练数据全部来自 Vikunja。留出集是按工具而非按应用划分的;Gitea 或 ParaBank 基准测试是下一个诚实的测试,而针对第二个目标运行
collect就是获得它的方法。第二个目标编译得很单薄。 Gitea 现在可以端到端编译——
createRepository和createIssue,均已验证,在其 issue 切片上 达到 2/13,对照swagger.v1.json。发现逻辑无需任何改动: 两个修复分别是确认类别(Gitea 通过在新 URL 上提供携带提交值的 结果来确认写入,而门控只查找横幅和正文回显)以及将容器/条目 URL 模式从cli.js移到配置中,APIC_SEEDS已经在那里。 标签和评论操作仍然缺失:它们位于词汇表未命名的控件之后, 而h对交给它的 14 个控件一个都没有命名。Vikunja 上召回率为 8/18。 缺失:桶创建、评论、关系和附件。
markTask大约每两次运行就失败一次(见结果)。效果是真实且 可观察到的;是否有任何东西确认它取决于任务现有的状态。 任何实时的npm run verify都应预期打印 8/9 或 9/9。并发运行会冲突。 每条命令共享
.apic/session.json中的一个 存储会话,因此同时启动的编译和验证可能会在运行中途摧毁彼此的 浏览器上下文(Error setting storage state: Execution context was destroyed)。作为变通方案,每次运行传入不同的APIC_SESSION; 真正的修复是默认按运行使用独立的会话文件。Watch 将所有失败都视为漂移。 真正的偶发失败与漂移分类 并不存在。三个误报类别是手工修复的——限流、 令牌过期和页面崩溃——但总体问题依然存在。
语义变化无法被检测,且很危险。 如果
deleteProject开始 归档而非删除,修复选择器就是错误的答案。 验证检查的是效果是否发生,而不是是否还是同样的效果。没有反向操作,因此套件会污染自己的夹具。重复运行 会使目标退化,直到被重置。
认证被绕过了。 一次登录、一个用户、没有权限范围——而这 正是真实企业软件中问题的难点所在。
先前工作声明
在黑客松期间从零编写。没有带入任何样板代码;仓库是在 活动当天早上空创建的。Playwright、MCP SDK 以及 OpenAI 和 fal 客户端是仅有的依赖。
许可证
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
Build your own tools from chat: design, test and host them on a shareable MCP server URL.
1Public, read-only MCP server for FarmNeural company facts, packages, and capabilities.
Related MCP Servers
- AlicenseCqualityAmaintenanceA production-grade MCP server that turns natural language intent into fully-architected, accessible, production-ready UI code through a 7-step agentic pipeline.201Apache 2.0
- FlicenseNot gradedqualityDmaintenanceAn extensible MCP server and generative UI engine for hosting interactive B2B enterprise workflows with dynamic styling and stateful simulators.-
- FlicenseNot gradedqualityBmaintenanceThis MCP server renders UI design artifacts headlessly, runs deterministic linters, and manages stateful design review loops with an independent vision critic.-
- AlicenseNot gradedqualityCmaintenanceA universal MCP server for registering internal, external, and OpenAPI-based APIs as MCP tools. It exposes them to MCP clients via Streamable HTTP and provides admin portal, RBAC/session auth, credential injection, and audit logging.Academic Free v1.1