运维诊断
- 作者仓库星标 0
- 作者更新于 实时读取
- 作者仓库 skills-registry
- 领域
- AI 智能
- 兼容 Agent
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- 信任分
- 88 / 100 · 社区维护
- 作者 / 版本 / 许可
- @tomevault-io · 未声明 license
- Token 消耗评级
- 较高消耗
- 接入复杂程度
- 需手动接入
- 是否需要外部 API Key
- 需要 · Vendor-specific
- 兼容的系统
- Docker
- 底层运行要求
- Python · Docker
- 文件与系统权限
-
- 只读
- 允许写入 / 修改
- Shell 执行
- 网络行为
- 允许外网请求
- 安装命令数
- 26 条
档案由构建时根据 SKILL.md 与安装命令自动衍生,可能与作者实际意图存在差异。
需要注意: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: noui-generalize
description: Use this skill when the user wants to generalize a recorded MCP workflow, rename tools to readab…
category: AI 智能
runtime: Python / Docker
---
# noui-generalize 输出预览
## PART A: 任务判断
- 适用问题:提示词、Agent 工作流、模型评估或自动化推理。
- 输入要求:目标材料、限制条件、期望输出和验收方式。
- 证据边界:围绕“Works for both MCP and Skill outputs / Critical Rules (Never Violate) / Phase 0 — Diagnose Execution Strategy”读取原文规则,不把推断写成作者承诺。
## PART B: 执行结果
- **01** 任务判断:确认你的需求是否属于提示词、Agent 工作流、模型评估或自动化推理,并标出输入、限制和预期结果。
- **02** 执行计划:优先按“Works for both MCP and Skill outputs / Critical Rules (Never Violate) / Phase 0 — Diagnose Execution Strategy”拆成步骤,说明每一步会读取什么、修改什么、产出什么。
- **03** 交付结果:给出可复制的命令、文件改动、检查清单或内容草稿,并说明如何继续迭代。
- **04** 风险边界:结合 读取文件、写入/修改文件、执行终端命令、会按任务需要访问外部网络、需要准备 Vendor-specific API Key 给出执行前确认项。
## Running Rules
- 读取文件、写入/修改文件、执行终端命令;会按任务需要访问外部网络;需要准备 Vendor-specific API Key。
- 先小样例验证,再放大到真实任务。
- 交付时同时给结果、检查口径和下一步迭代建议。 原文出现了 `/noui-record-workflow`、`/noui-record-login`、`/noui-generate-skill`、`/noui-generate-mcp`、`/credentials` 这类斜杠命令;如果你的 Agent 支持命令触发,优先用命令开场,再补充目标和边界。
告诉 Agent 目标文件或材料、期望结果、不可改范围、是否允许联网或执行命令。本 Skill 的权限画像是:读取文件、写入/修改文件、执行终端命令。
先用一个小任务确认它会围绕“Works for both MCP and Skill outputs / Critical Rules (Never Violate) / Phase 0 — Diagnose Execution Strategy”工作;涉及文件或命令时,先看 diff、日志、预览或测试结果。
检查最终产物是否包含明确结果、必要证据和下一步动作;如果输出泛泛而谈,就补充输入、边界和验收标准后重跑。
---
name: noui-generalize
description: Use this skill when the user wants to generalize a recorded MCP workflow, rename tools to readab…
category: AI 智能
source: tomevault-io/skills-registry
---
# noui-generalize
## 什么时候使用
- 把 AI / Agent方向的常用动作沉淀成 Agent 可调用的技能 适合处理AI Agent、提示词、模型评估与自动化推理,核心价值是把输入、判断、执行、验证和交付边界固定下来,避免 Agent 泛泛回答。 把任务拆成可执行、可检查…
- 面向提示词、Agent 工作流、模型评估或自动化推理,优先处理能明确输入、步骤和验收标准的工作。
## 需要提供什么
- 目标材料、目录范围、期望结果和不可改动内容。
- 是否允许联网、执行命令、读写文件或调用外部服务。
## 执行规则
- 围绕「Works for both MCP and Skill outputs / Critical Rules (Never Violate) / Phase 0 — Diagnose Execution Strategy」组织步骤,不把推断写成作者事实。
- 读取文件、写入/修改文件、执行终端命令;会按任务需要访问外部网络;需要准备 Vendor-specific API Key。
- 先跑小样例,确认结果可检查后再扩大任务范围。
## 输出要求
- 给出最终产物、关键证据、验证方式和下一步动作。
- 信息不足时标记 unknown,不编造命令、平台或依赖。 作者原文负责流程事实;仓库文件负责来源和命令;流狐只补充适用场景、限制和质量判断。
skill "noui-generalize" {
输入层 -> 用户目标 + 目标文件 + 禁止范围 + 验收标准
上下文层 -> Works for both MCP and Skill outputs / Critical Rules (Never Violate) / Phase 0 — Diagnose Execution Strategy
规则层 -> SKILL.md 触发条件 / 执行顺序 / 输出格式
运行层 -> Python / Docker | 读取文件、写入/修改文件、执行终端命令 | 会按任务需要访问外部网络
安全层 -> 需要准备 Vendor-specific API Key + 小任务验证 + diff / 日志复核
输出层 -> 可复制结果 + 检查清单 + 下一步迭代
} NoUI Generalize
Take a generated FastMCP server (or Skill) and make it work and usable:
- Execution strategy — generated servers execute inside the Tabby browser via CDP by default (see
/noui-record-workflow→ How Execution Works). This skill handles the edge cases: SPA content that needs navigation + DOM scraping, sites requiring HITL login, profiles that need credential-type fixes, or rare cases where the--execution-mode httpfallback is the right call. - Interface cleanup — replace raw internal API parameters (
f_sid,bl,reqid) with natural-language names (origin,destination,departure_date) so Claude Code can invoke tools without domain knowledge.
Prerequisite: /noui-record-workflow must be complete and the MCP server must exist under workbench/mcp_servers/. For authenticated sites, /noui-record-login must also be complete with a running Tabby session.
Works for both MCP and Skill outputs
The generalization logic is identical for either output format:
- MCP output: edit
workbench/mcp_servers/<app>/<server>/operations/<tool>.py— the body ofasync def execute(...). The FastMCP tool signature inserver.pymirrors the execute() signature, so renaming params also requires updating theserver.pydecorator args. - Skill output: edit
workbench/skills/<app>/operations/<tool>.py— the body ofasync def execute(...)and the_build_parser()argparse registrations, because the skill operation is a standalone CLI script. TheSKILL.mdbody's command examples also reference the flag names, so update them too (or regenerate SKILL.md by re-runningworkflow export --as skill --description-override "...").
The Phase 0 execution diagnosis (bot detection, empty credentials, profile promotion) applies to both outputs unchanged — both runtimes share the same noui_runtime/auth.py and the same Tabby credential flow.
After generalizing a skill, use /noui-generate-skill (not /noui-generate-mcp) for install / test.
Critical Rules (Never Violate)
- NEVER rewrite all tools at once — propose the new name and parameter list for each tool and get user approval before editing any files
- ALWAYS preserve existing execution mechanics (URL, method, headers, CDP vs. httpx) unless deliberately switching modes — only the Python function interface changes
- ALWAYS hardcode values that were static in the recording (session routing params, build labels,
bl,f_sid,reqid,soc_app, etc.) — do not expose infrastructure params to the caller - NEVER ask questions that can be answered by reading the code or URL
httpxonly appears in generated operations when--execution-mode httpwas used explicitly. Default generated operations usenoui_runtime.cdp.cdp_fetch. If you seehttpxin a default-mode server, something is wrong.- ALWAYS connect to the direct CDP port (
localhost:9222) forRuntime.evaluate— the Tabby relay (localhost:9223) only allows screencast and input events. - ALWAYS use
credentials: 'include'in browser-sidefetch()calls — the generatedcdp_fetchhelper already does this; preserve it when hand-editing. - NEVER assume "Login successful" in worker logs means login actually worked — CloakBrowser reports success when the DSL finishes, not when auth cookies appear. Always verify by checking cookies.
- After rewriting, always remind the user to restart Claude Code to reload the updated tools
Phase 0 — Diagnose Execution Strategy
Before touching tool names or params, check whether the tools actually work.
0a. Find and test the server
ls -lt workbench/mcp_servers/
Run a quick end-to-end test:
.venv/bin/python -c "
import asyncio, json, sys
sys.path.insert(0, 'workbench/mcp_servers/<app_slug>/<server_id>')
from operations.<tool_name> import execute
async def test():
result = await execute(...) # fill in sample params
print(json.dumps(result, indent=2)[:2000])
asyncio.run(test())
"
0b. Classify the result
| Result | Diagnosis | Next step |
|---|---|---|
| 200 with real data | Tool works — skip to Phase 2 (interface cleanup) | |
No Tabby page matching '<domain>' |
No tab open on the target site | Open the site in the Tabby browser, or tabby session ensure --profile <id> |
| Connection refused on 9222 | Tabby session not running | tabby session ensure --profile <id> |
CORS error / credentials include not allowed |
Cross-origin fetch blocked | Re-export with --execution-mode http |
| 429 / "Too Many Requests" from CDP | Very rare — Akamai flagging in-browser too | Phase 1 — verify the account, consider HITL re-login |
Server was generated with --execution-mode http and returns 429 |
httpx blocked by TLS fingerprinting | Re-export without --execution-mode http to land on the CDP default |
Empty credentials / name: "" (http mode only) |
Tabby credential_types format bug | Phase 1, Fix A |
No active profile found (http mode only) |
Profile still STAGING | Phase 1, Fix B |
0c. Confirm bot detection (if 429)
Verify the browser itself can make the call:
# Execute fetch from INSIDE the browser via CDP
import asyncio, json, websockets, httpx
async def test():
resp = await httpx.AsyncClient().get("http://localhost:9222/json", timeout=5)
targets = resp.json()
ws_url = [t["webSocketDebuggerUrl"] for t in targets if t["type"] == "page"][0]
js = """
fetch('/api/endpoint/test', { credentials: 'include' })
.then(r => r.text().then(t => JSON.stringify({status: r.status, body: t.substring(0, 200)})))
"""
async with websockets.connect(ws_url) as ws:
await ws.send(json.dumps({
"id": 1, "method": "Runtime.evaluate",
"params": {"expression": js, "awaitPromise": True, "returnByValue": True}
}))
resp = json.loads(await ws.recv())
print(resp["result"]["result"]["value"])
asyncio.run(test())
If 200 from browser but 429 from httpx → confirmed TLS fingerprinting. Proceed to Phase 1.
Phase 1 — Fix Execution Strategy
Fix A — credential_types format bug
After a fresh login register, the service_profiles table stores credential_types.cookies as a string array (["cookie_a", "cookie_b"]), but credentials.service.ts expects object arrays with .name. Every cookie comes back with empty name and value.
-- Run from tabby/ directory:
-- docker compose exec -T postgres psql -U browser_hitl -d browser_hitl
-- Check current format:
SELECT credential_types FROM service_profiles WHERE profile_id = '<profile>';
-- Fix: convert each string to {name, volatility} object:
UPDATE service_profiles SET credential_types = '{
"cookies": [
{"name": "EG_SESSIONTOKEN", "volatility": "STABLE"},
{"name": "bm_sz", "volatility": "VOLATILE"},
{"name": "ak_bmsc", "volatility": "VOLATILE"},
{"name": "user", "volatility": "STABLE"}
],
"headers": []
}'
WHERE profile_id = '<profile>';
Mark cookies that rotate frequently (Akamai tokens: bm_sz, ak_bmsc, bm_so, bm_s, _abck) as VOLATILE. Everything else is STABLE.
Fix B — Promote profile to ACTIVE
The credentials API (POST /credentials/request) only resolves ACTIVE profiles. Fresh registrations start as STAGING.
UPDATE service_profiles SET version_state = 'ACTIVE' WHERE profile_id = '<profile>';
Fix C — Set refresh_interval_seconds
The keepalive runner defaults to 3600s between artifact re-exports. Set lower for faster credential refresh:
UPDATE applications
SET export_policy = export_policy || '{"refresh_interval_seconds": 60}'::jsonb
WHERE name ILIKE '%<app_name>%';
Fix D — HITL login (when CloakBrowser fails)
If the automated login DSL can't complete (Akamai blocks form interactions), log in manually:
- Open Chrome →
chrome://inspect - Click "Configure..." → add
localhost:9222 - Click inspect on the page target
- In DevTools Console:
window.location = 'https://example.com/login' - Complete login manually via the screencast panel
- Force re-export:
UPDATE sessions SET artifacts_last_exported_at = NULL WHERE id = '<session_id>';
Port 9222 vs 9223: Port 9222 is the direct CDP endpoint (full access). Port 9223 is Tabby's relay that only allows
Page.screencast*andInput.dispatch*Event. Use 9222 for all workaround steps.
Fix E — CDP execution is the default (no rewrite needed)
CDP-based execution is now the default generated output. A recently exported
server already has operations that call find_page and cdp_fetch from
noui_runtime/cdp.py. If you are hand-editing or need to reason about the
pattern, the primitives are:
from noui_runtime.cdp import find_page, cdp_fetch
ws_url = await find_page("api.example.com") # match the recorded domain
result = await cdp_fetch( # fetch inside the browser
ws_url,
"https://api.example.com/v1/items",
method="POST",
headers={"Accept": "application/json"},
body={"name": "x"}, # JSON-encoded automatically
)
cdp_fetch always sets credentials: 'include'. The full specification lives
in compiler/runtime/cdp_adapter.py.
When you still need to re-generate or hand-edit:
- The server was generated with
--execution-mode httpand keeps getting 429. → Re-export without the flag to land on the CDP default. - You need SPA content that isn't exposed as a JSON API. → see DOM-scrape generalization below.
- The API truly cannot be reached from the browser origin (CORS, or
server-to-server endpoint). → keep
--execution-mode httpand fix the underlying auth / credential issue instead.
For the rationale (TLS fingerprinting, why 9222, why credentials: 'include'),
see /noui-record-workflow → How Execution Works.
DOM-scrape generalization (SPA-rendered content)
When data is rendered by client-side JS and not available as a JSON API, the
generated cdp_fetch path won't work — you need to navigate and scrape the
rendered DOM. This is a hand-written generalization on top of the CDP default:
import asyncio, json
import websockets
async def _navigate_and_scrape(ws_url: str, url: str, extract_js: str, wait_seconds: int = 7) -> dict:
"""Navigate to a URL, wait for SPA render, then extract data via JS."""
async with websockets.connect(ws_url) as ws:
await ws.send(json.dumps({
"id": 1, "method": "Page.navigate", "params": {"url": url}
}))
await ws.recv()
await asyncio.sleep(wait_seconds)
# Reuse the CDP runtime's eval helper — same session, same cookies
from noui_runtime.cdp import cdp_eval
return await cdp_eval(ws_url, extract_js)
To find the right DOM selectors, inspect data-stid or similar attributes:
# Discovery: list all data-stid values on the page
js = """
JSON.stringify([...new Set(
[...document.querySelectorAll('[data-stid]')]
.map(e => e.getAttribute('data-stid'))
)].sort())
"""
What to change when adding DOM-scrape to an operation:
- Keep
from noui_runtime.cdp import find_page, cdp_eval(the default module already exposes both). - Add
import asyncio, json, websocketsforPage.navigate. - Replace the
cdp_fetch(...)call with_navigate_and_scrape(ws_url, target_url, extract_js). - Extract the shape you need via a JS expression that returns
JSON.stringify(...).
Single browser instance caveat: Tabby runs one CloakBrowser per session. CDP navigation changes the page — if keepalive actions need the homepage, coordinate or set keepalive to
dom_checkonbodyonly.
Phase 2 — Understand the Server
Read
tools.json— note all tool names and their raw parameter names.Read each
operations/<name>.py— understand the call: URL, method, request body structure, which params are infrastructure vs. business inputs.Ask the user:
"What workflow did you record? Describe in plain language what you were doing — for example: 'I searched for flights from Fortaleza to Seattle on June 1 for 1 adult'."
Use the answer to anchor the business meaning of each tool.
Phase 3 — Close Gaps Per Tool
For each tool, ask only the questions needed to understand which parameters carry business input:
- "Which parameter in this tool controls [the thing you described]? Do you know what value you used during recording?"
- "This tool posts to
[endpoint]— is this the main [action]? What inputs does it need from the user?" - For opaque bodies: "The request body appears to be binary-encoded. Based on what you described (origin, destination, date), which of these raw params do you think carries each value?"
Skip questions that are answerable from the URL path, parameter names, or values visible in the code.
Phase 4 — Rewrite Tools One at a Time
For each tool, follow this sequence:
4a. Propose
Show the user the proposed new interface before touching any files:
Tool: create_data_batchexecute
New name: search_flights
New parameters:
- origin: str # IATA code or city name (e.g. "FOR", "Fortaleza")
- destination: str # IATA code or city name (e.g. "SEA", "Seattle")
- departure_date: str # Date in YYYY-MM-DD format
- passengers: int = 1 # Number of passengers
Hardcoded (from recording):
- f_sid: "<value from recording>"
- bl: "<value>"
- reqid: <value>
- soc_app: <value>
Execution: CDP fetch / httpx (state which)
Approve this? (yes / adjust: ...)
4b. Edit on approval
Once approved, make four edits:
operations/<old_name>.py— rewrite the function signature and body:- New function name matches the approved tool name
- New parameters are natural-language (
origin,destination, etc.) - Infrastructure params are hardcoded as local variables
- Build the raw request from the natural params (string formatting, encoding)
- Use CDP fetch if Phase 1 flagged bot detection; otherwise preserve the existing HTTP call
tools.json— update the entry:name→ new tool namedescription→ plain-English description of what it doesparameters→ updated list with natural-language names, types, descriptions
server.py— update the import and tool registration to use the new function name (if the function was renamed)API.md— refresh the documentation:
.venv/bin/python cli/main.py mcp docs <server_id>
This overwrites API.md from the current tools.json. Run it after every tool edit, not just at the end.
4c. Move to next tool
Repeat Phase 4 for each tool. Do not batch edits.
Phase 5 — Iterate After Testing
After all tools are rewritten, do a final docs refresh:
.venv/bin/python cli/main.py mcp docs <server_id>
Then tell the user:
"Done.
API.mdis up to date. Please restart Claude Code (close and reopen, or run/reconnect) to reload the updated tools. Then try invoking the workflow — for example: 'search for flights from Fortaleza to Seattle on June 1'."
When the user reports results, fix any issues:
| Problem | Fix |
|---|---|
| Wrong parameter mapping | Re-read the operation, ask clarifying question, re-propose |
| Missing required parameter | Add it to signature and body |
| Tool call fails with HTTP error | Read the error body, compare to original recording values |
| Body encoding wrong | Check if original body was URL-encoded, JSON, or protobuf — reconstruct accordingly |
| 429 from tool call on a CDP-default server | Verify Tabby has an authenticated tab open; if yes, the account may be flagged — redo HITL login (Phase 1, Fix D) |
429 from tool call on --execution-mode http server |
Re-export without the flag to use the CDP default |
| CDP connection refused | Tabby session not running — tabby session ensure --profile <id> |
fetch() returns 403 inside browser |
Session expired — redo HITL login (Phase 1, Fix D) |
Keep iterating until the user can successfully invoke the workflow using natural language.
What to Hardcode vs. Expose as Parameters
| Hardcode | Expose as parameter |
|---|---|
f_sid, bl, reqid, soc_app |
Origin, destination, dates |
| Build labels, session routing tokens | Search inputs (keywords, quantities) |
| CSRF tokens captured during recording | Filters (cabin class, stops) |
| App version identifiers | User preferences the tool is supposed to accept |
Infrastructure headers (x-goog-ext-*) |
Any value that changes meaningfully per call |
When in doubt: if the value was the same every time during recording and doesn't carry user intent, hardcode it.
Dynamic-Header-Injection Sites (JS-injected auth)
Some SPAs acquire a short-lived bearer token in-memory (fetch from /auth/init or similar on page load), then wrap window.fetch / XMLHttpRequest with an interceptor that sets Authorization: Bearer <jwt> right before every XHR. The cookie jar is empty of auth material, localStorage / sessionStorage don't hold the bearer either, yet every real API call carries it. Calling the API from cdp_fetch with credentials: 'include' alone will get you a 401 or 403.
Detect
- HAR contains an
Authorization: Bearer eyJ...header whose JWTjti(orsub,exp) rotates between recordings of the same flow. document.cookieat runtime does not include the bearer substring.localStorageandsessionStoragedumps don't contain the bearer either.- The recorded API host is on a different subdomain from
www.*(e.g.api-prod-*,api.*).
Workaround — CDP Network-event sniffing
Enable CDP's Network domain, reload the page, listen for Network.requestWillBeSent, and grab the Authorization off the first request that matches your target host. Cache in /tmp with a TTL, invalidate on 401/403, retry once.
Reference implementation (10-line core):
await ws.send({"id": 1, "method": "Network.enable"})
await ws.send({"id": 2, "method": "Page.reload"})
deadline = loop.time() + 20
while loop.time() < deadline:
msg = json.loads(await ws.recv())
if msg.get("method") != "Network.requestWillBeSent":
continue
url = msg["params"]["request"].get("url", "")
if TARGET_HOST not in url:
continue
authz = {k.lower(): v for k, v in msg["params"]["request"].get("headers", {}).items()}.get("authorization", "")
if authz.startswith("eyJ"):
return authz
See .claude/skills/indigo-flight-search/noui_runtime/indigo_auth.py for the full cache + retry wrapper around this core. The IndiGo skill is the canonical example of this pattern in this repo.
Forward pointer: When the retro-C1 generic
sniff_headers()helper lands innoui_runtime, delete the hand-written module and replace the import. Seeplans/noui/noui-agent-friction-retro-plan.mdSection C1.
Per-host static client IDs
Sites of this shape often pair the dynamic JWT with a stable user_key / x-api-key / x-client-id header that is per-host but constant across sessions (look for the same value across every request to that host in the HAR). Hardcode those — they're infrastructure, not user input.
Known Anti-Bot Sites
| Site | Bot Detection | CloakBrowser Login | Workaround |
|---|---|---|---|
| Expedia | Akamai Bot Manager | Form blocked silently; reaches homepage but can't submit | HITL login + CDP fetch + DOM scrape |
| Generic SPA with JS-injected auth | Token not in cookies/localStorage; Authorization is set by a fetch interceptor in the page's JS bundle |
n/a (site may be anonymous) | Sniff the Authorization via CDP Network.requestWillBeSent on page reload; cache per session. See Dynamic-Header-Injection Sites above. |
IndiGo (api-prod-*-skyplus6e.goindigo.in) |
Anonymous session; per-host user_key + rotating JWT; Akamai cookies |
n/a | Hardcode per-host user_keys; sniff JWT at runtime. Reference: indigo-flight-search skill. |
Architecture Notes
- Why cookies alone fail: Akamai fingerprints the TLS ClientHello, HTTP/2 settings frame, header order, and other transport-layer signals.
httpx/curlhave distinctly different fingerprints from Chrome, even with identical cookies. - Why CDP works:
Runtime.evaluate+fetch()executes inside the real Chrome process. The HTTP request goes through Chrome's network stack with its native TLS implementation and fingerprint. - Port 9222 vs 9223: Port 9222 is the direct CDP endpoint (full access). Port 9223 is Tabby's relay proxy that only allows
Page.startScreencast,Page.stopScreencast,Page.screencastFrameAck, andInput.dispatch*Event. Use 9222 for this workaround. - Single browser instance: Tabby runs one CloakBrowser per session. CDP navigation changes the page for that session — if keepalive needs the homepage, set keepalive health checks to
dom_checkonbodyrather than URL-based checks.
Decision Flow
Start
│
Phase 0: Test the tool as-is
│
├─ Works (200 with data)?
│ └─ Skip to Phase 2 (interface cleanup)
│
├─ "No Tabby page matching <domain>"?
│ └─ Open the site in Tabby, or `tabby session ensure --profile <id>`
│
├─ CORS / cross-origin error?
│ └─ Re-export with `--execution-mode http`
│
├─ 429 on a default-mode server?
│ └─ Phase 1: Fix D (HITL re-login) — the session or account is flagged
│
├─ 429 on an `--execution-mode http` server?
│ └─ Re-export without the flag to land on the CDP default
│
├─ Empty credentials / name: "" (http mode)?
│ └─ Phase 1: Fix A (credential_types format)
│
├─ "No active profile" (http mode)?
│ └─ Phase 1: Fix B (promote to ACTIVE)
│
├─ Login didn't actually work?
│ └─ Phase 1: Fix D (HITL login)
│
├─ 401/403 on default CDP server, cookies appear correct?
│ └─ Check HAR: is `Authorization` present and does its JWT `jti` rotate across recordings?
│ └─ Yes → Dynamic-header-injection pattern; see *Dynamic-Header-Injection Sites* section
│
Phase 2: Read tools.json + operations + ask user about workflow
│
Phase 3: Close gaps per tool (targeted questions only)
│
Phase 4: Rewrite tools one at a time (propose → approve → edit)
│
Phase 5: Test, iterate, remind user to restart Claude Code
Related Skills
/noui-record-login— Record login and register with Tabby (run first for authenticated sites)/noui-record-workflow— Record and export the workflow (run first to generate the server)/noui-generate-mcp— Server lifecycle after generalization (start/stop/connect to Claude Code)
Source: adoptai/noui — distributed by TomeVault.
先判断是否适合
作者设计意图
作者的方法与取舍
边界和复核