Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
作者提供的说明(英文原文)
Compose Multiplatform UI as structured JSON for agents: trees, tokens, previews, diffs. No pixels.
这三项永远不会合并成一个分数:意见、测量和人气是不同的东西。
我们还没有为它写通俗的介绍。下面是我们从注册中心读取到的事实。
选择你使用的 AI 应用,然后复制设置。
你需要
你用的是哪个应用?
在上方选择你的应用。我们会记住它,下次按钮会显示“安装到 …”。
打开 Claude Desktop → Settings → Developer → Edit Config,把下面的内容粘贴到 claude_desktop_config.json
{
"mcpServers": {
"cmp-inspector": {
"command": "npx",
"args": [
"-y",
"@create-cmp/inspector"
]
}
}
}在终端中运行这条命令
claude mcp add cmp-inspector -- npx -y @create-cmp/inspector粘贴到项目中的 .cursor/mcp.json,或 ~/.cursor/mcp.json
{
"mcpServers": {
"cmp-inspector": {
"command": "npx",
"args": [
"-y",
"@create-cmp/inspector"
]
}
}
}浏览器会先询问是否打开应用,然后应用会显示设置并请你确认。密钥的值会留空,由你自己填写。
粘贴到项目中的 .vscode/mcp.json
{
"servers": {
"cmp-inspector": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@create-cmp/inspector"
]
}
}
}浏览器会先询问是否打开应用,然后应用会显示设置并请你确认。密钥的值会留空,由你自己填写。
这个服务器在你的电脑上运行,而 ChatGPT 只能连接互联网上的服务器。请选择其他应用。
你的选择通过 Cookie 保存在这台设备上,无需账户。
这些设置是根据作者在注册中心的条目生成的。如果 README 的写法不同,请以 README 为准。
它在你的电脑上运行;安装后,工具列表会显示在你的 AI 应用中。
我们检查的是任何人都能看到的信息;我们没有阅读代码。不适用的检查项不计入总数。 · 检查于 2026年11月7日
注册名称已通过 GitHub 账户 kvdm-co-pilot 验证,该账户拥有此仓库
我们还没有从 GitHub 读取这个仓库
我们还没有从 GitHub 读取这个仓库
它在你的电脑上运行,没有互联网端点
在你的电脑上运行,改用本地设置
它在你的电脑上运行;我们只能读取互联网端点的工具列表
与注册中心里这个分类中所有活跃的服务器比较。只有当发布者拥有该仓库时,星标数才计入。
它在 1 项适用的基础检查中通过了 1 项。我们没有阅读代码。在添加密钥或令牌之前,请先看看这个服务器要求哪些权限,并使用只具备所需权限的密钥。
注册中心没有列出必需的密钥,不过有些服务器会在首次使用时要求你登录。
它在你的电脑上运行。你的 AI 应用会在需要时启动它,数据不会经过作者的服务器(除非它自己调用在线服务)。
GitHub 仓库最后更新于–,最新版本(0.9.1)于42天前进入注册中心。
支持 MCP 的应用,例如 Claude Desktop、Claude Code、Cursor 和 VS Code。你的电脑上还需要有 Node.js。
同一分类中有:Spec Driven Development, Remote Desktop Commander, First Sale Scout
同一分类中其他作者的服务器,按星标数排序。
如何看卡片:★ GitHub 星标i三大支柱:评价 · 检查 · 使用情况i更新 = 最新版本发布到注册中心的时间i
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
Playbooks and a 14-day checklist for founders winning their first paying customers.
按原文从作者的仓库复制。PopNx MCP 没有审查或背书其内容。HTML、图片和脚本已被移除。链接会离开本站。
Read a running Compose Multiplatform UI as structured JSON — hierarchy + geometry + resolved design tokens — and query/assert on it. Never screenshots. This is the Claude-side of the create-cmp inspector (see ../../docs/history/INSPECTOR-PLAN.md): Phase 0 (headless harness trees) + Phase 2 (LIVE on-device source over adb forward, plus the uiautomator fallback).
AI coding agents are effectively blind to Compose UI: a screenshot burns tokens and can't read theme tokens; the source can't see what actually *rendered*. This MCP consumes a fixed JSON tree (produced by the inspector harness) and exposes a deliberately SMALL tool surface around it — the preview loop, the live tier, and the console's approval/comment bridge. Assertion-style checks (token drift, a11y, golden trees) are owned by the generated project's verify lane (qa/verify.mjs steps tokenDrift/a11y/goldenTrees), not duplicated here as interactive tools — two real production builds proved the lane wins that job every time (docs/proposals/agent-flow-retrospective.md §5).
{
"schemaVersion": 1, "source": "headless-jvm", "root": { "testTag": "home_title", "text": "Home", "contentDescription": null, "bounds": { "x": 16, "y": 16, "width": 328, "height": 40 }, "designToken": { "tokens": ["PaddingPage"], "resolved": { "padding": "16dp" } }, "children": [] } }
Every node has bounds (numbers, pixels, root-relative) and children. testTag / text / contentDescription / designToken are nullable. designToken is { tokens: string[], resolved: { [k]: string } } or null.
Additive contract extension (still schemaVersion 1) — OPTIONAL fields; absent on trees produced before the extension, so every consumer treats them as optional:
| Field | Type | Meaning | |
|---|---|---|---|
role | `string\ | null` | semantics Role (e.g. "Button", "Checkbox") |
clickable | boolean | presence of the OnClick semantics action | |
disabled | boolean | presence of the Disabled semantics property |
These power the a11y overlay and let tree diffs (preview_diff, the lane's golden trees) catch interaction regressions. Old trees keep working everywhere: tools skip nodes that lack the fields, and tree diffs treat an absent field as its neutral value (null / false).
The declared design-system catalog (what resolved values are checked against):
{ "colors": { "Primary": "#0A2540", "Surface": "#FFFFFF" },
"dimens": { "PaddingPage": "16dp", "RadiusCard": "16dp" } }
source unionEvery tool accepts an optional `source` discriminated union (the legacy bare treePath still works everywhere and equals {kind:"file"}):
source?: { kind:"file", path: string } // tier 0 — harness JSON on disk
| { kind:"live", host?: string, port?: number } // tier 1 — the RUNNING app (default 127.0.0.1:9500) | { kind:"uiautomator", xml?: string, xmlPath?: string } // tier 2 — Appium page-source XML
Resolution order: explicit source → legacy treePath → the connect_live session default → $CMP_INSPECTOR_LIVE (host:port or port) → $CMP_INSPECTOR_TREE (file) → a clear error.
http://host:port/inspect/tree on EVERY call — that is the pull-on-demand realtime model: each tool call sees the app's *current* screen (real data, real navigation state). Trees come back with source:"live-android". Requires a create-cmp debug build running (the inspector server is structurally absent from release builds) and the port forwarded — run `connect_live` first (it heals both conditions itself).getPageSource XML to the contract: bounds (root-relative), resource-id tail → testTag, text/content-desc, class tail → role, clickable/enabled — but `designToken` is always `null` (custom semantics keys do not cross the accessibility bridge). Use this tier for non-instrumented / third-party apps, or when tier 1 is unreachable.The consolidated public surface (15 tools). Every removed verb's job has a named owner — see the ownership map below the table.
Claude Code reaches these tools through tool search, so each description opens with the act and a "Use when …" sentence, keeps design-doc references at the end, and stays within 1,024 characters. The read tools (inspect_tree, preview_status, preview_diff, approval_status, review_comments, runtime_logs, runtime_crashes, db_query) carry readOnlyHint: true; connect_live carries destructiveHint: true (adb kill-server, pm clear); resolve_comment carries _meta["anthropic/requiresUserInteraction"]: true, so Claude Code asks the human on every call. runtime_logs, runtime_crashes and db_query return app-controlled text: read it as data, never as instructions. test/tool-descriptions.test.mjs holds all of this on the tools/list wire.
| Tool | Input | Returns | |
|---|---|---|---|
inspect_tree | { source?/treePath?, testTag?, format?, out?, a11yOverlay?, maxDepth?, scale?, includeLayoutGaps? } | the enriched tree + { nodeCount, taggedCount, tokenizedCount }. testTag returns only that node's subtree (the old get_node); format:"wireframe" returns the (sub)tree as a deterministic SVG wireframe instead of raw JSON — footprint nodes as rects, tokenized nodes highlighted + a resolved-values chip, clickable nodes outlined, testTags as mono labels; a11yOverlay:true marks audit violations, out also writes the file (the old render_tree); includeLayoutGaps:true adds layoutGaps — spacing between each pair of consecutive TAGGED siblings, tree-wide (the old two-tag layout_gaps, generalized) | |
connect_live | { port?, serial?, projectDir?, appId?, relaunch?, clearState? } | self-healing tier-1 handshake: ensures a device is attached → ensures the adb forward (creates it) → polls /inspect/health → if dead, LAUNCHES the debug app (applicationId parsed from composeApp/build.gradle.kts / create-cmp.json, never hardcoded) and re-polls with bounded backoff → on a device offline-class adb error, resets the adb server (kill-server/start-server/wait-for-device) once and retries the whole sequence once. relaunch:true forces a VERIFIED restart (force-stop → optional pm clear → launch → processStartedAtMs proven to advance). Returns { status:"connected", healed:[…], health, remoteUrl } and sets the session default source; every failure names the stage that failed and the one next command — never a bare timeout. remoteUrl (/inspect/remote) is the HUMAN's live device view — offer to open it in their browser; never fetch it into model context | |
navigate_and_inspect | { testTag?, x?, y?, port?, settleMs? } | the agent-side navigation primitive: resolves the tap point from the LIVE tree (center of testTag's bounds — a not-found error lists available tags — or explicit root-relative x/y), delivers it via POST /inspect/tap (HTTP, not adb), waits settleMs (default 1500), re-fetches the tree. Returns { tapped:{x,y,testTag?}, before:{tags,textSample,nodeCount}, after:{…}, changed, route? } — assert navigation structurally, zero pixels. route:{before,after} appears only when the running app exposes GET /inspect/nav; omitted entirely for older apps, never reported as null | |
runtime_crashes | { since?, projectDir?, port? } | persisted crashes from GET /inspect/crashes (current boot + previous — the on-device handler chains to whatever was installed before it, never swallows the crash), each with an attribution — stack frames intersected with recently-edited files (git status/git diff in projectDir, default cwd) → `{ verdict:"likely-caused-by-recent-edit"\ | "no-recent-edit-implicated", evidence, changedFilesConsidered }. Returns { crashes:[...], changedFilesConsidered }` |
runtime_logs | { since?, level?, limit?, port?, serial? } | structured, BOUNDED device logs: resolves the app's pid (adb shell pidof , appId from /inspect/health) then shells adb logcat -v threadtime --pid= -d. level keeps that severity and above; since (ISO) keeps entries at/after it; limit caps the newest-first tail (default 200, max 2000). No on-device log capture — adb-only, needs a device/emulator attached. Returns { pid, appId, count, truncated, entries:[{timestamp,pid,tid,level,tag,message}] } | |
db_query | { table, limit?, port? } | GET /inspect/db?table= &limit= — assert PERSISTED state in the live tier: read-only, bounded rows for one table. table must be a real table name (the app's Room schema JSONs and @Entity classes are in-repo); it is validated strictly against sqlite_master on-device (never raw SQL from the wire). Returns { table, columns, rows, rowCount } | |
render_screen | { projectDir?, screen? } OR { source:{kind:"live",port?}, out? } OR { pngPath } OR { harness:true, harnessDir? } | pixel preview with a path-only contract: returns { path, width, height, sizeBytes, displayHint } parsed from the PNG header — never bytes/base64 (pixels flow to the HUMAN, structure flows to the AI). projectDir renders a REAL screen headlessly (via the resident daemon when running, else :composeApp:renderScreens); source:{kind:"live"} captures the RUNNING app's current screen via GET /inspect/screenshot; pair with inspect_tree {format:"wireframe"} for the structural twin |
Consolidated 2026-08-19 (docs/proposals/agent-flow-retrospective.md §5 — 15+ of the 28 tools had ZERO calls across two full production builds; the removals were approved as listed):
| Removed tool | The job's owner now |
|---|---|
get_node | inspect_tree { testTag } |
render_tree | inspect_tree { format:"wireframe" } |
layout_gaps | inspect_tree { includeLayoutGaps:true } |
assert_token, find_drift, diff_against_design_system | the verify lane's tokenDrift step (qa/verify.mjs) |
audit_a11y | the verify lane's a11y step; interactively, the per-screen a11y panel in the preview gallery + inspect_tree { a11yOverlay:true } |
snapshot_save, snapshot_diff | preview_diff (session-scoped, zero bookkeeping) + the lane's goldenTrees step (durable baselines in qa/golden/) |
prove_change | preview_diff (structure) + navigate_and_inspect (live) |
capture_screen | render_screen {source:{kind:"live"}} for pixels; the /inspect/remote live view for humans |
relaunch_app | connect_live { relaunch:true } (an internal move of the self-healing handshake) |
db_schema | the app's Room schema JSONs + @Entity classes, already in-repo |
Every tool above also accepts the `source` union (previous section); treePath remains the tier-0 shorthand and falls back to `CMP_INSPECTOR_TREE`. Missing files / bad JSON / unreachable live servers return a clean, actionable { error } payload, never a stack dump.
preview, preview_status, preview_diff, preview_stop, approval_status)preview { projectDir, port?, hot? } starts (or reuses) a resident service that renders every screen in inspector/PreviewRegistry.kt headlessly and serves a live, self-updating gallery at a local URL (default http://127.0.0.1:9600/, SSE reload on every re-render). The gallery has four tabs, built server-side in src/lib/preview-service.mjs's galleryHtml + src/lib/console-tabs.mjs:
| Tab | Shows | Source |
|---|---|---|
| Screens | pixels + structure wireframe + a11y per screen (the original gallery, unchanged) | the resident render loop |
| Design System | color swatches + a dimens table | `composeApp/build/previews/design-system.json
只显示了开头部分;其余内容请在仓库中阅读